diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 2b1a60623dc..fcfddf75b72 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -2,7 +2,7 @@ module Idv module VerifyInfoConcern extend ActiveSupport::Concern - def update + def shared_update return if idv_session.verify_info_step_document_capture_session_uuid analytics.idv_doc_auth_verify_submitted(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). @@ -48,13 +48,7 @@ def update double_address_verification: capture_secondary_id_enabled, ) - # Don't allow the user to go back to document capture after verifying - if flow_session['redo_document_capture'] - flow_session.delete('redo_document_capture') - flow_session[:flow_path] ||= 'standard' - end - - redirect_to after_update_url + return true end private diff --git a/app/controllers/idv/gpo_controller.rb b/app/controllers/idv/gpo_controller.rb index 4e7f6d9b122..7140bb9f87c 100644 --- a/app/controllers/idv/gpo_controller.rb +++ b/app/controllers/idv/gpo_controller.rb @@ -98,10 +98,7 @@ def confirmation_maker_perform end def send_reminder - current_user.confirmed_email_addresses.each do |email_address| - UserMailer.with(user: current_user, email_address: email_address). - letter_reminder.deliver_now_or_later - end + current_user.send_email_to_all_addresses(:letter_reminder) end def pii_locked? diff --git a/app/controllers/idv/gpo_verify_controller.rb b/app/controllers/idv/gpo_verify_controller.rb index d4464d78cc5..80f63eadd52 100644 --- a/app/controllers/idv/gpo_verify_controller.rb +++ b/app/controllers/idv/gpo_verify_controller.rb @@ -51,21 +51,26 @@ def create return end - if result.extra[:pending_in_person_enrollment] - redirect_to idv_in_person_ready_to_verify_url - else - prepare_for_personal_key + prepare_for_personal_key - redirect_to idv_personal_key_url - end + redirect_to idv_personal_key_url end private + def pending_in_person_enrollment? + return false unless IdentityConfig.store.in_person_proofing_enabled + current_user.pending_in_person_enrollment.present? + end + + def account_not_ready_to_be_activated? + fraud_check_failed? || pending_in_person_enrollment? + end + def prepare_for_personal_key - event, _disavowal_token = create_user_event(:account_verified) + unless account_not_ready_to_be_activated? + event, _disavowal_token = create_user_event(:account_verified) - if !fraud_check_failed? UserAlerts::AlertUserAboutAccountVerified.call( user: current_user, date_time: event.created_at, diff --git a/app/controllers/idv/in_person/verify_info_controller.rb b/app/controllers/idv/in_person/verify_info_controller.rb index c09e2dc8cad..787d2b967e9 100644 --- a/app/controllers/idv/in_person/verify_info_controller.rb +++ b/app/controllers/idv/in_person/verify_info_controller.rb @@ -8,7 +8,6 @@ class VerifyInfoController < ApplicationController include VerifyInfoConcern include OutageConcern - before_action :renders_404_if_flag_not_set before_action :confirm_ssn_step_complete before_action :confirm_verify_info_step_needed before_action :check_for_outage, only: :show @@ -36,6 +35,17 @@ def show process_async_state(load_async_state) end + def update + success = shared_update + + if success + # Mark the FSM verify step completed. This is for the 50/50 state + flow_session['Idv::Steps::InPerson::VerifyStep'] = true + + redirect_to idv_in_person_verify_info_url + end + end + private # state_id_type is hard-coded here because it's required for proofing against @@ -50,18 +60,10 @@ def invalid_state? pii.blank? end - def after_update_url - idv_in_person_verify_info_url - end - def prev_url idv_in_person_step_url(step: :ssn) end - def renders_404_if_flag_not_set - render_not_found unless IdentityConfig.store.in_person_verify_info_controller_enabled - end - def pii @pii = flow_session[:pii_from_user] end @@ -73,11 +75,23 @@ def flow_session def analytics_arguments { - flow_path: flow_path, + flow_path: flow_session[:flow_path], step: 'verify', analytics_id: 'In Person Proofing', irs_reproofing: irs_reproofing?, - }.merge(**acuant_sdk_ab_test_analytics_args) + }.merge(**acuant_sdk_ab_test_analytics_args). + merge(**extra_analytics_properties) + end + + def extra_analytics_properties + extra = { + pii_like_keypaths: [[:same_address_as_id], [:state_id, :state_id_jurisdiction]], + } + unless flow_session.dig(:pii_from_user, :same_address_as_id).nil? + extra[:same_address_as_id] = + flow_session[:pii_from_user][:same_address_as_id].to_s == 'true' + end + extra end end end diff --git a/app/controllers/idv/review_controller.rb b/app/controllers/idv/review_controller.rb index 2cb7ad61914..abb564e7492 100644 --- a/app/controllers/idv/review_controller.rb +++ b/app/controllers/idv/review_controller.rb @@ -92,6 +92,7 @@ def init_profile idv_session.create_profile_from_applicant_with_password(password) if idv_session.address_verification_mechanism == 'gpo' + current_user.send_email_to_all_addresses(:letter_reminder) analytics.idv_gpo_address_letter_enqueued(enqueued_at: Time.zone.now, resend: false) end diff --git a/app/controllers/idv/verify_info_controller.rb b/app/controllers/idv/verify_info_controller.rb index f88706d7c39..ac3afbbe809 100644 --- a/app/controllers/idv/verify_info_controller.rb +++ b/app/controllers/idv/verify_info_controller.rb @@ -34,15 +34,25 @@ def show process_async_state(load_async_state) end + def update + success = shared_update + + if success + # Don't allow the user to go back to document capture after verifying + if flow_session['redo_document_capture'] + flow_session.delete('redo_document_capture') + flow_session[:flow_path] ||= 'standard' + end + + redirect_to idv_verify_info_url + end + end + private # state ID type isn't manually set for Idv::VerifyInfoController def set_state_id_type; end - def after_update_url - idv_verify_info_url - end - def prev_url idv_ssn_url end diff --git a/app/controllers/two_factor_authentication/otp_verification_controller.rb b/app/controllers/two_factor_authentication/otp_verification_controller.rb index 0f9eb65d4e4..2e56b55708f 100644 --- a/app/controllers/two_factor_authentication/otp_verification_controller.rb +++ b/app/controllers/two_factor_authentication/otp_verification_controller.rb @@ -147,11 +147,6 @@ def post_analytics(result) end end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def analytics_properties parsed_phone = Phonelib.parse(phone) @@ -165,7 +160,6 @@ def analytics_properties phone_configuration_id: phone_configuration&.id, in_multi_mfa_selection_flow: in_multi_mfa_selection_flow?, enabled_mfa_methods_count: mfa_context.enabled_mfa_methods_count, - sign_up_mfa_priority_bucket: sign_up_mfa_selection_order_bucket, } end diff --git a/app/controllers/users/backup_code_setup_controller.rb b/app/controllers/users/backup_code_setup_controller.rb index 194d73ecf69..5ce1e0bb794 100644 --- a/app/controllers/users/backup_code_setup_controller.rb +++ b/app/controllers/users/backup_code_setup_controller.rb @@ -24,10 +24,7 @@ def index def create generate_codes result = BackupCodeSetupForm.new(current_user).submit - analytics_properties = result.to_h.merge( - sign_up_mfa_selection_order_bucket: - sign_up_mfa_selection_order_bucket, - ) + analytics_properties = result.to_h analytics.backup_code_setup_visit(**analytics_properties) irs_attempts_api_tracker.mfa_enroll_backup_code(success: result.success?) @@ -79,7 +76,6 @@ def mfa_user def track_backup_codes_confirmation_setup_visit analytics.multi_factor_auth_enter_backup_code_confirmation_visit( enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, ) end @@ -93,11 +89,6 @@ def generate_codes user_session[:backup_codes] = @codes end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def set_backup_code_setup_presenter @presenter = SetupPresenter.new( current_user: current_user, diff --git a/app/controllers/users/mfa_selection_controller.rb b/app/controllers/users/mfa_selection_controller.rb index f2f153e2b07..011ee40e8df 100644 --- a/app/controllers/users/mfa_selection_controller.rb +++ b/app/controllers/users/mfa_selection_controller.rb @@ -10,19 +10,13 @@ class MfaSelectionController < ApplicationController def index two_factor_options_form @after_setup_path = after_mfa_setup_path - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) @presenter = two_factor_options_presenter - analytics.user_registration_2fa_additional_setup_visit( - sign_up_mfa_priority_bucket: @sign_up_mfa_selection_order_bucket, - ) + analytics.user_registration_2fa_additional_setup_visit end def update result = submit_form - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - analytics_hash = result.to_h.merge( - sign_up_mfa_priority_bucket: @sign_up_mfa_selection_order_bucket, - ) + analytics_hash = result.to_h analytics.user_registration_2fa_additional_setup(**analytics_hash) if result.success? @@ -45,7 +39,6 @@ def submit_form def two_factor_options_presenter TwoFactorOptionsPresenter.new( user_agent: request.user_agent, - priority_bucket: @sign_up_mfa_selection_order_bucket, user: current_user, phishing_resistant_required: service_provider_mfa_policy.phishing_resistant_required?, piv_cac_required: service_provider_mfa_policy.piv_cac_required?, diff --git a/app/controllers/users/phone_setup_controller.rb b/app/controllers/users/phone_setup_controller.rb index cf0043a895c..e261974ed7a 100644 --- a/app/controllers/users/phone_setup_controller.rb +++ b/app/controllers/users/phone_setup_controller.rb @@ -43,16 +43,10 @@ def recaptcha_enabled? FeatureManagement.phone_recaptcha_enabled? end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def track_phone_setup_visit mfa_user = MfaContext.new(current_user) analytics.user_registration_phone_setup_visit( enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, ) end diff --git a/app/controllers/users/piv_cac_authentication_setup_controller.rb b/app/controllers/users/piv_cac_authentication_setup_controller.rb index 9506c2e2733..e4ac9e625d4 100644 --- a/app/controllers/users/piv_cac_authentication_setup_controller.rb +++ b/app/controllers/users/piv_cac_authentication_setup_controller.rb @@ -64,11 +64,6 @@ def track_piv_cac_setup_visit ) end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def remove_piv_cac revoke_remember_device(current_user) current_user_id = current_user.id @@ -170,7 +165,6 @@ def analytics_properties { in_multi_mfa_selection_flow: in_multi_mfa_selection_flow?, enabled_mfa_methods_count: mfa_context.enabled_mfa_methods_count, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, } end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 16539f6c8a6..aec4b1dbada 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -10,13 +10,11 @@ class SessionsController < Devise::SessionsController rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_signin - skip_before_action :session_expires_at, only: %i[active keepalive] skip_before_action :require_no_authentication, only: [:new] before_action :store_sp_metadata_in_session, only: [:new] before_action :check_user_needs_redirect, only: [:new] before_action :apply_secure_headers_override, only: [:new, :create] before_action :clear_session_bad_password_count_if_window_expired, only: [:create] - after_action :add_csrf_token_header_to_response, only: [:keepalive] def new override_csp_for_google_analytics @@ -50,31 +48,6 @@ def destroy super end - def active - session[:pinged_at] = now - Rails.logger.debug(alive?: alive?, expires_at: expires_at) - render json: { live: alive?, timeout: expires_at } - end - - def keepalive - session[:session_expires_at] = now + Devise.timeout_in if alive? - analytics.session_kept_alive if alive? - - render json: { live: alive?, timeout: expires_at } - end - - def timeout - analytics.session_timed_out - request_id = sp_session[:request_id] - sign_out - flash[:info] = t( - 'notices.session_timedout', - app_name: APP_NAME, - minutes: IdentityConfig.store.session_timeout_in_minutes, - ) - redirect_to root_url(request_id: request_id) - end - private def clear_session_bad_password_count_if_window_expired @@ -143,24 +116,10 @@ def handle_valid_authentication redirect_to next_url_after_valid_authentication end - def now - @now ||= Time.zone.now - end - - def expires_at - session[:session_expires_at]&.to_datetime || (now - 1) - end - def browser_is_ie11? BrowserCache.parse(request.user_agent).ie?(11) end - def alive? - return false unless session && expires_at - session_alive = expires_at > now - current_user.present? && session_alive - end - def track_authentication_attempt(email) user = User.find_with_email(email) || AnonymousUser.new diff --git a/app/controllers/users/totp_setup_controller.rb b/app/controllers/users/totp_setup_controller.rb index a24df21b0e7..6ecc8a94545 100644 --- a/app/controllers/users/totp_setup_controller.rb +++ b/app/controllers/users/totp_setup_controller.rb @@ -66,18 +66,12 @@ def set_totp_setup_presenter ) end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def track_event mfa_user = MfaContext.new(current_user) analytics.totp_setup_visit( user_signed_up: MfaPolicy.new(current_user).two_factor_enabled?, totp_secret_present: new_totp_secret.present?, enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, ) end @@ -144,7 +138,6 @@ def current_auth_app_count def analytics_properties { in_multi_mfa_selection_flow: in_multi_mfa_selection_flow?, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, pii_like_keypaths: [[:mfa_method_counts, :phone]], } end diff --git a/app/controllers/users/two_factor_authentication_setup_controller.rb b/app/controllers/users/two_factor_authentication_setup_controller.rb index f8cdfcceff8..ad5bb38f037 100644 --- a/app/controllers/users/two_factor_authentication_setup_controller.rb +++ b/app/controllers/users/two_factor_authentication_setup_controller.rb @@ -9,19 +9,13 @@ class TwoFactorAuthenticationSetupController < ApplicationController def index two_factor_options_form - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) @presenter = two_factor_options_presenter - analytics.user_registration_2fa_setup_visit( - sign_up_mfa_priority_bucket: @sign_up_mfa_selection_order_bucket, - ) + analytics.user_registration_2fa_setup_visit end def create result = submit_form - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - analytics_hash = result.to_h.merge( - sign_up_mfa_priority_bucket: @sign_up_mfa_selection_order_bucket, - ) + analytics_hash = result.to_h analytics.user_registration_2fa_setup(**analytics_hash) irs_attempts_api_tracker.mfa_enroll_options_selected( success: result.success?, @@ -49,7 +43,6 @@ def submit_form def two_factor_options_presenter TwoFactorOptionsPresenter.new( user_agent: request.user_agent, - priority_bucket: @sign_up_mfa_selection_order_bucket, user: current_user, phishing_resistant_required: service_provider_mfa_policy.phishing_resistant_required?, piv_cac_required: service_provider_mfa_policy.piv_cac_required?, diff --git a/app/controllers/users/webauthn_setup_controller.rb b/app/controllers/users/webauthn_setup_controller.rb index 798f973e27b..e6ef10c4ac3 100644 --- a/app/controllers/users/webauthn_setup_controller.rb +++ b/app/controllers/users/webauthn_setup_controller.rb @@ -103,11 +103,6 @@ def set_webauthn_setup_presenter ) end - def sign_up_mfa_selection_order_bucket - return unless in_multi_mfa_selection_flow? - @sign_up_mfa_selection_order_bucket = AbTests::SIGN_UP_MFA_SELECTION.bucket(current_user.uuid) - end - def flash_error(errors) flash.now[:error] = errors.values.first.first end @@ -178,7 +173,6 @@ def process_valid_webauthn(form) def analytics_properties { in_multi_mfa_selection_flow: in_multi_mfa_selection_flow?, - sign_up_mfa_selection_order_bucket: sign_up_mfa_selection_order_bucket, } end diff --git a/app/helpers/session_timeout_warning_helper.rb b/app/helpers/session_timeout_warning_helper.rb index 46ea99bdb5c..03d2a15a3bf 100644 --- a/app/helpers/session_timeout_warning_helper.rb +++ b/app/helpers/session_timeout_warning_helper.rb @@ -11,10 +11,6 @@ def session_timeout_warning IdentityConfig.store.session_timeout_warning_seconds end - def expires_at - session[:session_expires_at]&.to_datetime || Time.zone.now - 1 - end - def timeout_refresh_path UriService.add_params( request.original_fullpath, diff --git a/app/mailers/report_mailer.rb b/app/mailers/report_mailer.rb index aacd9d463e9..584b460d783 100644 --- a/app/mailers/report_mailer.rb +++ b/app/mailers/report_mailer.rb @@ -16,9 +16,4 @@ def system_demand_report(email:, data:, name:) attachments['system_demand.csv'] = data mail(to: email, subject: t('report_mailer.system_demand_report.subject')) end - - def warn_error(email:, error:, env: Rails.env) - @error = error - mail(to: email, subject: "[#{env}] identity-idp error: #{error.class.name}") - end end diff --git a/app/models/profile.rb b/app/models/profile.rb index 690c48a9936..87cad4cdba4 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -67,11 +67,11 @@ def activate(reason_deactivated: nil) end # rubocop:enable Rails/SkipsModelValidations - def confirm_that_profile_can_be_activated! + def reason_not_to_activate if pending_reasons.any? - raise "Attempting to activate profile with pending reasons: #{pending_reasons.join(',')}" + "Attempting to activate profile with pending reasons: #{pending_reasons.join(',')}" elsif deactivation_reason.present? - raise "Attempting to activate profile with deactivation reason: #{deactivation_reason}" + "Attempting to activate profile with deactivation reason: #{deactivation_reason}" end end @@ -218,6 +218,10 @@ def irs_attempts_api_tracker private + def confirm_that_profile_can_be_activated! + raise reason_not_to_activate if reason_not_to_activate + end + def track_fraud_review_adjudication(decision:) if IdentityConfig.store.irs_attempt_api_track_idv_fraud_review fraud_review_request = user.fraud_review_requests.last diff --git a/app/models/user.rb b/app/models/user.rb index 1cebb21ba08..844efef05f6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -106,6 +106,32 @@ def gpo_verification_pending_profile? gpo_verification_pending_profile.present? end + def suspended? + suspended_at.to_s > reinstated_at.to_s + end + + def reinstated? + reinstated_at.to_s > suspended_at.to_s + end + + def suspend! + if suspended? + analytics.user_suspended(success: false, error_message: :user_already_suspended) + raise 'user_already_suspended' + end + update!(suspended_at: Time.zone.now) + analytics.user_suspended(success: true) + end + + def reinstate! + if !suspended? + analytics.user_reinstated(success: false, error_message: :user_is_not_suspended) + raise 'user_is_not_suspended' + end + update!(reinstated_at: Time.zone.now) + analytics.user_reinstated(success: true) + end + def pending_profile return @pending_profile if defined?(@pending_profile) @@ -403,6 +429,20 @@ def send_confirmation_instructions add_method_tracer :send_devise_notification, "Custom/#{name}/send_devise_notification" + def analytics + @analytics ||= Analytics.new(user: self, request: nil, session: {}, sp: nil) + end + + def send_email_to_all_addresses(user_mailer_template) + confirmed_email_addresses.each do |email_address| + UserMailer.with( + user: self, + email_address: email_address, + ).send(user_mailer_template). + deliver_now_or_later + end + end + private def lockout_period diff --git a/app/presenters/two_factor_options_presenter.rb b/app/presenters/two_factor_options_presenter.rb index 6b9c8168afc..897781b2f8a 100644 --- a/app/presenters/two_factor_options_presenter.rb +++ b/app/presenters/two_factor_options_presenter.rb @@ -1,28 +1,19 @@ class TwoFactorOptionsPresenter include ActionView::Helpers::TranslationHelper - attr_reader :user, :priority_bucket + attr_reader :user - def initialize(user_agent:, user: nil, priority_bucket: :default, + def initialize(user_agent:, user: nil, phishing_resistant_required: false, piv_cac_required: false) @user_agent = user_agent @user = user - @priority_bucket = priority_bucket @phishing_resistant_required = phishing_resistant_required @piv_cac_required = piv_cac_required end def options - if priority_bucket == :authentication_app_priority - totp_option + webauthn_platform_option + webauthn_option + piv_cac_option + - phone_options + backup_code_option - elsif priority_bucket == :usability_priority - phone_options + totp_option + backup_code_option + webauthn_platform_option + - webauthn_option + piv_cac_option - else - webauthn_platform_option + webauthn_option + piv_cac_option + totp_option + - phone_options + backup_code_option - end + totp_option + phone_options + webauthn_platform_option + + backup_code_option + webauthn_option + piv_cac_option end def icon diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index f4e23b1d289..3eee12765dd 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -3608,17 +3608,14 @@ def user_prompted_before_navigation_and_still_on_page(path:, seconds:, **extra) # @param [Boolean] success # @param [Hash] errors - # @param [String] sign_up_mfa_priority_bucket # Tracks when the the user has selected and submitted additional MFA methods on user registration def user_registration_2fa_additional_setup(success:, - sign_up_mfa_priority_bucket:, errors: nil, **extra) track_event( 'User Registration: Additional 2FA Setup', { success: success, - sign_up_mfa_priority_bucket: sign_up_mfa_priority_bucket, errors: errors, **extra, }.compact, @@ -3626,12 +3623,9 @@ def user_registration_2fa_additional_setup(success:, end # Tracks when user visits additional MFA selection page - # @param [String] sign_up_mfa_priority_bucket - def user_registration_2fa_additional_setup_visit(sign_up_mfa_priority_bucket:, **extra) + def user_registration_2fa_additional_setup_visit track_event( 'User Registration: Additional 2FA Setup visited', - sign_up_mfa_priority_bucket: sign_up_mfa_priority_bucket, - **extra, ) end @@ -3640,11 +3634,9 @@ def user_registration_2fa_additional_setup_visit(sign_up_mfa_priority_bucket:, * # @param [Integer] enabled_mfa_methods_count # @param [Integer] selected_mfa_count # @param ['voice', 'auth_app'] selection - # @param [String] sign_up_mfa_priority_bucket # Tracks when the the user has selected and submitted MFA auth methods on user registration def user_registration_2fa_setup( success:, - sign_up_mfa_priority_bucket:, errors: nil, selected_mfa_count: nil, enabled_mfa_methods_count: nil, @@ -3658,7 +3650,6 @@ def user_registration_2fa_setup( errors: errors, selected_mfa_count: selected_mfa_count, enabled_mfa_methods_count: enabled_mfa_methods_count, - sign_up_mfa_priority_bucket: sign_up_mfa_priority_bucket, selection: selection, **extra, }.compact, @@ -3666,12 +3657,9 @@ def user_registration_2fa_setup( end # Tracks when user visits MFA selection page - # @param [String] sign_up_mfa_priority_bucket - def user_registration_2fa_setup_visit(sign_up_mfa_priority_bucket:, **extra) + def user_registration_2fa_setup_visit track_event( 'User Registration: 2FA Setup visited', - sign_up_mfa_priority_bucket: sign_up_mfa_priority_bucket, - **extra, ) end @@ -3876,6 +3864,42 @@ def user_registration_user_fully_registered( ) end + # Tracks when user reinstated + # @param [Boolean] success + # @param [String] error_message + def user_reinstated( + success:, + error_message: nil, + **extra + ) + track_event( + 'User Suspension: Reinstated', + { + success: success, + error_message: error_message, + **extra, + }.compact, + ) + end + + # Tracks when user suspended + # @param [Boolean] success + # @param [String] error_message + def user_suspended( + success:, + error_message: nil, + **extra + ) + track_event( + 'User Suspension: Suspended', + { + success: success, + error_message: error_message, + **extra, + }.compact, + ) + end + # Tracks when USPS in-person proofing enrollment is created # @param [String] enrollment_code # @param [Integer] enrollment_id diff --git a/app/services/idv/profile_maker.rb b/app/services/idv/profile_maker.rb index f2d5ad3f188..4992b16c276 100644 --- a/app/services/idv/profile_maker.rb +++ b/app/services/idv/profile_maker.rb @@ -10,7 +10,6 @@ def initialize(applicant:, user:, user_password:, initiating_service_provider: n end def save_profile( - active:, fraud_review_needed:, gpo_verification_needed:, deactivation_reason: nil @@ -20,7 +19,6 @@ def save_profile( profile.encrypt_pii(pii_attributes, user_password) profile.proofing_components = current_proofing_components profile.save! - profile.activate if active profile.deactivate_for_gpo_verification if gpo_verification_needed profile.deactivate_for_fraud_review if fraud_review_needed profile diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index 3b871a5caf3..90f8bc7515c 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -48,11 +48,13 @@ def respond_to_missing?(method_sym, include_private) def create_profile_from_applicant_with_password(user_password) profile_maker = build_profile_maker(user_password) profile = profile_maker.save_profile( - active: deactivation_reason.nil?, deactivation_reason: deactivation_reason, fraud_review_needed: threatmetrix_failed_and_needs_review?, gpo_verification_needed: gpo_verification_needed?, ) + + profile.activate unless profile.reason_not_to_activate + self.pii = profile_maker.pii_attributes self.profile_id = profile.id self.personal_key = profile.personal_key diff --git a/app/services/service_provider_seeder.rb b/app/services/service_provider_seeder.rb index cfe816c2732..fee5f47d928 100644 --- a/app/services/service_provider_seeder.rb +++ b/app/services/service_provider_seeder.rb @@ -80,12 +80,6 @@ def check_for_missing_sps extra_sp_error = ExtraServiceProviderError.new( "Extra service providers found in DB: #{extra_sps.join(', ')}", ) - - if IdentityConfig.store.team_ursula_email.present? - ReportMailer.warn_error( - email: IdentityConfig.store.team_ursula_email, - error: extra_sp_error, - ).deliver_now_or_later - end + NewRelic::Agent.notice_error(extra_sp_error) end end diff --git a/app/views/idv/gpo_verify/index.html.erb b/app/views/idv/gpo_verify/index.html.erb index f901c9e1267..40612a17bb5 100644 --- a/app/views/idv/gpo_verify/index.html.erb +++ b/app/views/idv/gpo_verify/index.html.erb @@ -9,6 +9,18 @@ <% title t('forms.verify_profile.title') %> +<%= render AlertComponent.new(type: :info, class: 'margin-bottom-4', text_tag: 'div') do %> +

+ <%= t('forms.verify_profile.alert_info') %> +
+ <%= render 'shared/address', address: @gpo_verify_form.pii %> +

+

+ <%= t('forms.verify_profile.wrong_address') %> + <%= link_to t('forms.verify_profile.clear_and_start_over'), idv_confirm_start_over_path %> +

+<% end %> + <%= render PageHeadingComponent.new.with_content(t('forms.verify_profile.welcome_back')) %> <%= sanitize t('forms.verify_profile.welcome_back_description'), tags: %w[p strong] %>
diff --git a/app/views/idv/verify_info/new.html.erb b/app/views/idv/verify_info/new.html.erb deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/views/report_mailer/warn_error.text.erb b/app/views/report_mailer/warn_error.text.erb deleted file mode 100644 index d114ae14356..00000000000 --- a/app/views/report_mailer/warn_error.text.erb +++ /dev/null @@ -1,5 +0,0 @@ -**Error**: -<%= @error.class.name %> - -**Message**: -<%= @error.message %> diff --git a/config/application.yml.default b/config/application.yml.default index 6d7010a08e2..2497649d566 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -320,12 +320,10 @@ session_timeout_warning_seconds: 150 session_total_duration_timeout_in_minutes: 720 ses_configuration_set_name: '' set_remember_device_session_expiration: false -sign_up_mfa_selection_order_testing: '{ "default": 100, "authentication_app_priority": 0, "usability_priority": 0 }' sp_handoff_bounce_max_seconds: 2 show_user_attribute_deprecation_warnings: false otp_min_attempts_remaining_warning_count: 3 system_demand_report_email: 'foo@bar.com' -team_ursula_email: '' test_ssn_allowed_list: '' totp_code_interval: 30 unauthorized_scope_enabled: false diff --git a/config/initializers/ab_tests.rb b/config/initializers/ab_tests.rb index 8230fd81f42..361813ce4f3 100644 --- a/config/initializers/ab_tests.rb +++ b/config/initializers/ab_tests.rb @@ -34,11 +34,6 @@ def self.in_person_cta_variant_testing_buckets buckets end - SIGN_UP_MFA_SELECTION = AbTestBucket.new( - experiment_name: 'MFA selection order: Auth app first', - buckets: IdentityConfig.store.sign_up_mfa_selection_order_testing, - ) - IN_PERSON_CTA = AbTestBucket.new( experiment_name: 'In-Person Proofing CTA', buckets: in_person_cta_variant_testing_buckets, diff --git a/config/locales/forms/en.yml b/config/locales/forms/en.yml index 06407a3a434..9fa2d31a8f3 100644 --- a/config/locales/forms/en.yml +++ b/config/locales/forms/en.yml @@ -134,6 +134,8 @@ en: validation: required_checkbox: Please check this box to continue verify_profile: + alert_info: 'We sent a lettter with your one-time code to:' + clear_and_start_over: Clear your information and start over instructions: Enter the 10-character code from the letter you received. name: One-time code return_to_profile: Return to your profile @@ -144,6 +146,7 @@ en: your one-time code below.

If your letter hasn’t arrived yet, please be patient as letters typically take 3 to 7 business days to arrive. Thank you for your patience.

' + wrong_address: Not the right address? webauthn_delete: caution: If you remove your security key you won’t be able to use it to access your %{app_name} account. diff --git a/config/locales/forms/es.yml b/config/locales/forms/es.yml index 92fe063975d..7e6facd7f85 100644 --- a/config/locales/forms/es.yml +++ b/config/locales/forms/es.yml @@ -142,6 +142,8 @@ es: validation: required_checkbox: Marque esta casilla para continuar verify_profile: + alert_info: 'Enviamos una carta con su código único a:' + clear_and_start_over: Borrar su información y empezar de nuevo instructions: Introduzca el código de 10 caracteres de la carta que ha recibido. name: Código único return_to_profile: Regrese a su perfil @@ -152,6 +154,7 @@ es: único a continuación.

Si su carta aún no ha llegado, tenga paciencia, ya que las cartas suelen tardar de 3 a 7 días hábiles en llegar. Gracias por su paciencia.

' + wrong_address: ¿La dirección no es correcta? webauthn_delete: caution: Si elimina su clave de seguridad, no podrá usarla para acceder a su cuenta %{app_name}. diff --git a/config/locales/forms/fr.yml b/config/locales/forms/fr.yml index bb9eaf67407..778f8b04348 100644 --- a/config/locales/forms/fr.yml +++ b/config/locales/forms/fr.yml @@ -143,6 +143,8 @@ fr: validation: required_checkbox: Veuillez cocher cette case pour continuer verify_profile: + alert_info: 'Nous avons envoyé une lettre avec votre code à usage unique à:' + clear_and_start_over: Supprimez vos données et recommencez instructions: Entrez le code à 10 caractères figurant sur la lettre que vous avez reçue. name: Code à usage unique @@ -155,6 +157,7 @@ fr: encore arrivée, veuillez être patient car les lettres prennent généralement entre trois à sept jours ouvrables pour arriver. Nous vous remercions de votre patience.

' + wrong_address: Pas la bonne adresse? webauthn_delete: caution: Si vous supprimez votre clé de sécurité, vous ne pourrez plus l’utiliser pour accéder à votre compte %{app_name}. diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index 23976eacca7..776cef5abd5 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -11,7 +11,7 @@ en: continue_plain: Continue mail: resend: Send another letter - send: Send a letter + send: Request a letter cancel: actions: account_page: Go to account page @@ -238,7 +238,7 @@ en: come_back_later: Your letter is on the way mail: resend: Send another letter? - verify: Want a letter? + verify: Verify your address otp_delivery_method: How should we send a code? review: Review and submit session: diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index 8de261b44d7..bb07b27c64e 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -12,7 +12,7 @@ es: continue_plain: Continuar mail: resend: Enviar otra carta - send: Enviar una carta + send: Solicitar una carta cancel: actions: account_page: Ir a la página de la cuenta @@ -254,7 +254,7 @@ es: come_back_later: Su carta está en camino mail: resend: '¿Enviar otra carta?' - verify: '¿Desea una carta?' + verify: Verifique su dirección otp_delivery_method: '¿Cómo debemos enviar un código?' review: Revise y envíe session: diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml index 32ee3c3b972..603663db939 100644 --- a/config/locales/idv/fr.yml +++ b/config/locales/idv/fr.yml @@ -12,7 +12,7 @@ fr: continue_plain: Continuer mail: resend: Envoyer une autre lettre - send: Envoyer une lettre + send: Demander une lettre cancel: actions: account_page: Accéder à la page de votre compte @@ -271,7 +271,7 @@ fr: come_back_later: Votre lettre est en route mail: resend: Envoyer une autre lettre? - verify: Vous voulez une lettre? + verify: Vérifiez votre adresse otp_delivery_method: Comment envoyer un code? review: Réviser et soumettre session: diff --git a/config/routes.rb b/config/routes.rb index 4db943f727f..e13fa8ef638 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -84,8 +84,6 @@ post '/' => 'users/sessions#create', as: :user_session get '/logout' => 'users/sessions#destroy', as: :destroy_user_session delete '/logout' => 'users/sessions#destroy' - get '/active' => 'users/sessions#active' - post '/sessions/keepalive' => 'users/sessions#keepalive' get '/login/piv_cac' => 'users/piv_cac_login#new' get '/login/piv_cac_error' => 'users/piv_cac_login#error' @@ -138,7 +136,6 @@ get '/reauthn' => 'mfa_confirmation#new', as: :user_password_confirm post '/reauthn' => 'mfa_confirmation#create', as: :reauthn_user_password - get '/timeout' => 'users/sessions#timeout' end if IdentityConfig.store.enable_test_routes @@ -394,22 +391,26 @@ get '/in_person/:step' => 'in_person#show', as: :in_person_step put '/in_person/:step' => 'in_person#update' - # deprecated routes - get '/confirmations' => 'personal_key#show' - post '/confirmations' => 'personal_key#update' - end + get '/by_mail' => 'gpo_verify#index', as: :gpo_verify + post '/by_mail' => 'gpo_verify#create' + get '/by_mail/confirm_start_over' => 'confirm_start_over#index', + as: :confirm_start_over - get '/account/verify' => 'idv/gpo_verify#index', as: :idv_gpo_verify - post '/account/verify' => 'idv/gpo_verify#create' - get '/account/verify/confirm_start_over' => 'idv/confirm_start_over#index', as: :idv_confirm_start_over - if FeatureManagement.gpo_verification_enabled? - scope '/verify', module: 'idv', as: 'idv' do + if FeatureManagement.gpo_verification_enabled? get '/usps' => 'gpo#index', as: :gpo put '/usps' => 'gpo#create' post '/usps' => 'gpo#update' end + + # deprecated routes + get '/confirmations' => 'personal_key#show' + post '/confirmations' => 'personal_key#update' end + # Old paths to GPO outside of IdV. + get '/account/verify', to: redirect('/verify/by_mail') + get '/account/verify/confirm_start_over', to: redirect('/verify/by_mail/confirm_start_over') + root to: 'users/sessions#new' end diff --git a/lib/identity_config.rb b/lib/identity_config.rb index da8eddeffd5..239a5960f2b 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -424,15 +424,10 @@ def self.build_store(config_map) config.add(:ses_configuration_set_name, type: :string) config.add(:set_remember_device_session_expiration, type: :boolean) config.add(:show_user_attribute_deprecation_warnings, type: :boolean) - config.add( - :sign_up_mfa_selection_order_testing, type: :json, - options: { symbolize_names: true } - ) config.add(:skip_encryption_allowed_list, type: :json) config.add(:sp_handoff_bounce_max_seconds, type: :integer) config.add(:state_tracking_enabled, type: :boolean) config.add(:system_demand_report_email, type: :string) - config.add(:team_ursula_email, type: :string) config.add(:telephony_adapter, type: :string) config.add(:test_ssn_allowed_list, type: :comma_separated_string_list) config.add(:totp_code_interval, type: :integer) diff --git a/lib/identity_job_log_subscriber.rb b/lib/identity_job_log_subscriber.rb index 2adc8c6d3ab..fd4e901d49b 100644 --- a/lib/identity_job_log_subscriber.rb +++ b/lib/identity_job_log_subscriber.rb @@ -118,7 +118,13 @@ def logger end def self.worker_logger - @worker_logger ||= ActiveSupport::Logger.new(Rails.root.join('log', 'workers.log')) + return @worker_logger if defined?(@worker_logger) + + if FeatureManagement.log_to_stdout? + @worker_logger = ActiveSupport::Logger.new(STDOUT) + else + @worker_logger = ActiveSupport::Logger.new(Rails.root.join('log', 'workers.log')) + end end private diff --git a/spec/controllers/idv/gpo_controller_spec.rb b/spec/controllers/idv/gpo_controller_spec.rb index 7a06cc0d416..c6286253ea1 100644 --- a/spec/controllers/idv/gpo_controller_spec.rb +++ b/spec/controllers/idv/gpo_controller_spec.rb @@ -214,9 +214,7 @@ def expect_resend_letter_to_send_letter_and_redirect(otp:) expect(gpo_confirmation_maker).to receive(:perform) expect(gpo_confirmation_maker).to receive(:otp) if otp - - put :create - + expect { put :create }.to change { ActionMailer::Base.deliveries.count }.by(1) expect(response).to redirect_to idv_come_back_later_path end end diff --git a/spec/controllers/idv/gpo_verify_controller_spec.rb b/spec/controllers/idv/gpo_verify_controller_spec.rb index 3487bdcb443..534894f7ebd 100644 --- a/spec/controllers/idv/gpo_verify_controller_spec.rb +++ b/spec/controllers/idv/gpo_verify_controller_spec.rb @@ -152,6 +152,15 @@ end context 'with establishing in person enrollment' do + let!(:enrollment) do + create( + :in_person_enrollment, + :pending, + user: user, + profile: pending_profile, + ) + end + let(:proofing_components) do ProofingComponent.create(user: user, document_check: Idp::Constants::Vendors::USPS) end @@ -162,7 +171,7 @@ and_return(user.pending_profile.decrypt_pii(user.password).to_h) end - it 'redirects to ready to verify screen' do + it 'redirects to personal key page' do expect(@analytics).to receive(:track_event).with( 'IdV: GPO verification submitted', success: true, @@ -177,7 +186,7 @@ action - expect(response).to redirect_to(idv_in_person_ready_to_verify_url) + expect(response).to redirect_to(idv_personal_key_url) end it 'does not dispatch account verified alert' do diff --git a/spec/controllers/idv/in_person/verify_info_controller_spec.rb b/spec/controllers/idv/in_person/verify_info_controller_spec.rb index 50c76b382ec..835349a6f97 100644 --- a/spec/controllers/idv/in_person/verify_info_controller_spec.rb +++ b/spec/controllers/idv/in_person/verify_info_controller_spec.rb @@ -3,7 +3,7 @@ RSpec.describe Idv::InPerson::VerifyInfoController do include IdvHelper - let(:pii_from_user) { Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN.dup } + let(:pii_from_user) { Idp::Constants::MOCK_IDV_APPLICANT_SAME_ADDRESS_AS_ID.dup } let(:flow_session) do { 'document_capture_session_uuid' => 'fd14e181-6fb1-4cdc-92e0-ef66dad0df4e', :pii_from_user => pii_from_user, @@ -39,15 +39,6 @@ :confirm_verify_info_step_needed, ) end - - it 'renders 404 if feature flag not set' do - allow(IdentityConfig.store).to receive(:in_person_verify_info_controller_enabled). - and_return(false) - - get :show - - expect(response).to be_not_found - end end context 'when in_person_verify_info_controller_enabled' do @@ -67,6 +58,8 @@ flow_path: 'standard', irs_reproofing: false, step: 'verify', + same_address_as_id: true, + pii_like_keypaths: [[:same_address_as_id], [:state_id, :state_id_jurisdiction]], } end diff --git a/spec/controllers/idv/personal_key_controller_spec.rb b/spec/controllers/idv/personal_key_controller_spec.rb index 15b50436cee..5bed10555bf 100644 --- a/spec/controllers/idv/personal_key_controller_spec.rb +++ b/spec/controllers/idv/personal_key_controller_spec.rb @@ -13,7 +13,6 @@ def stub_idv_session user_password: password, ) profile = profile_maker.save_profile( - active: false, fraud_review_needed: false, gpo_verification_needed: false, ) diff --git a/spec/controllers/idv/review_controller_spec.rb b/spec/controllers/idv/review_controller_spec.rb index e7a0d2c6d80..998331d3770 100644 --- a/spec/controllers/idv/review_controller_spec.rb +++ b/spec/controllers/idv/review_controller_spec.rb @@ -668,6 +668,15 @@ def show expect(profile).to_not be_active end + it 'sends an email about the gpo letter' do + expect do + put :create, + params: { + user: { password: ControllerHelper::VALID_PASSWORD }, + } + end.to(change { ActionMailer::Base.deliveries.count }.by(1)) + end + it 'redirects to come back later page' do put :create, params: { user: { password: ControllerHelper::VALID_PASSWORD } } diff --git a/spec/controllers/idv_controller_spec.rb b/spec/controllers/idv_controller_spec.rb index db46011e71f..6152750a02e 100644 --- a/spec/controllers/idv_controller_spec.rb +++ b/spec/controllers/idv_controller_spec.rb @@ -113,7 +113,7 @@ end it 'redirects to account recovery if user has a password reset profile' do - profile = create(:profile, :password_reset) + profile = create(:profile, :verified, :password_reset) stub_sign_in(profile.user) allow(subject.reactivate_account_session).to receive(:started?).and_return(true) diff --git a/spec/controllers/openid_connect/authorization_controller_spec.rb b/spec/controllers/openid_connect/authorization_controller_spec.rb index 2c5ebe6f9de..2ad50a04c8a 100644 --- a/spec/controllers/openid_connect/authorization_controller_spec.rb +++ b/spec/controllers/openid_connect/authorization_controller_spec.rb @@ -164,7 +164,7 @@ end context 'profile is reset' do - let(:user) { create(:profile, :password_reset).user } + let(:user) { create(:profile, :verified, :password_reset).user } it 'redirects to have the user enter their personal key' do action @@ -278,7 +278,7 @@ end context 'profile is reset' do - let(:user) { create(:profile, :password_reset).user } + let(:user) { create(:profile, :verified, :password_reset).user } it 'redirects to the redirect_uri immediately without proofing' do IdentityLinker.new(user, service_provider).link_identity(ial: 1) diff --git a/spec/controllers/reactivate_account_controller_spec.rb b/spec/controllers/reactivate_account_controller_spec.rb index 2b8931d4975..a37f61eb43f 100644 --- a/spec/controllers/reactivate_account_controller_spec.rb +++ b/spec/controllers/reactivate_account_controller_spec.rb @@ -14,7 +14,7 @@ describe '#index' do context 'with a password reset profile' do - let(:profiles) { [create(:profile, :password_reset)] } + let(:profiles) { [create(:profile, :verified, :password_reset)] } it 'renders the index template' do get :index @@ -34,7 +34,7 @@ end describe '#update' do - let(:profiles) { [create(:profile, :password_reset)] } + let(:profiles) { [create(:profile, :verified, :password_reset)] } it 'redirects user to idv_url' do put :update diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index bbe363ac307..3904e5e911b 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -614,7 +614,7 @@ def name_id_version(format_urn) context 'with IAL2 and the profile is reset' do it 'redirects to IdV URL for IAL2 proofer' do - user = create(:profile, :password_reset).user + user = create(:profile, :verified, :password_reset).user generate_saml_response(user, ial2_settings) expect(response).to redirect_to reactivate_account_path 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 7624b1a4787..7c0d8cd9df8 100644 --- a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb @@ -60,7 +60,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_priority_bucket: :default, } expect(@analytics).to receive(:track_event). @@ -133,7 +132,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_priority_bucket: :default, } stub_analytics @@ -201,7 +199,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_priority_bucket: :default, } stub_analytics @@ -265,7 +262,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_priority_bucket: :default, } stub_analytics @@ -446,7 +442,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_priority_bucket: :default, } expect(@analytics).to receive(:track_event). @@ -532,7 +527,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: false, - sign_up_mfa_priority_bucket: nil, } expect(@analytics).to have_received(:track_event). @@ -616,7 +610,6 @@ phone_fingerprint: Pii::Fingerprinter.fingerprint(parsed_phone.e164), enabled_mfa_methods_count: 0, in_multi_mfa_selection_flow: false, - sign_up_mfa_priority_bucket: nil, } expect(@analytics).to have_received(:track_event). diff --git a/spec/controllers/users/backup_code_setup_controller_spec.rb b/spec/controllers/users/backup_code_setup_controller_spec.rb index d1674d2591a..a338eb9741c 100644 --- a/spec/controllers/users/backup_code_setup_controller_spec.rb +++ b/spec/controllers/users/backup_code_setup_controller_spec.rb @@ -32,7 +32,6 @@ pii_like_keypaths: [[:mfa_method_counts, :phone]], error_details: nil, enabled_mfa_methods_count: 1, - sign_up_mfa_selection_order_bucket: nil, }) expect(@analytics).to receive(:track_event). with('Backup Code Created', { diff --git a/spec/controllers/users/phone_setup_controller_spec.rb b/spec/controllers/users/phone_setup_controller_spec.rb index 5c9410352c5..f4706ef42b3 100644 --- a/spec/controllers/users/phone_setup_controller_spec.rb +++ b/spec/controllers/users/phone_setup_controller_spec.rb @@ -24,8 +24,7 @@ expect(@analytics).to receive(:track_event). with('User Registration: phone setup visited', - { enabled_mfa_methods_count: 0, - sign_up_mfa_selection_order_bucket: nil }) + { enabled_mfa_methods_count: 0 }) expect(NewPhoneForm).to receive(:new).with( user:, analytics: kind_of(Analytics), diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index bd1aa408154..b2e986c0daf 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -14,85 +14,6 @@ end end - describe 'GET /active' do - context 'when user is present' do - before do - stub_sign_in - end - - it 'returns a 200 status code' do - get :active - - expect(response.status).to eq(200) - end - - it 'renders json' do - get :active - - expect(response.media_type).to eq('application/json') - end - - it 'sets live key to true' do - controller.session[:session_expires_at] = Time.zone.now + 10 - get :active - - json ||= JSON.parse(response.body) - - expect(json['live']).to eq true - end - - it 'includes the timeout key', freeze_time: true do - timeout = Time.zone.now + 10 - controller.session[:session_expires_at] = timeout - get :active - - json ||= JSON.parse(response.body) - - expect(json['timeout'].to_datetime.to_i).to eq(timeout.to_i) - end - end - - context 'when user is not present' do - it 'sets live key to false' do - get :active - - json ||= JSON.parse(response.body) - - expect(json['live']).to eq false - end - - it 'includes session_expires_at', freeze_time: true do - get :active - - json ||= JSON.parse(response.body) - - expect(json['timeout'].to_datetime.to_i).to eq(Time.zone.now.to_i - 1) - end - - it 'updates the pinged_at session key' do - stub_sign_in - now = Time.zone.now - expected_time = now + 10 - session[:pinged_at] = now - - travel_to(expected_time) do - get :active - end - - expect(session[:pinged_at].to_i).to eq(expected_time.to_i) - end - end - - it 'does not track analytics event' do - stub_sign_in - stub_analytics - - expect(@analytics).to_not receive(:track_event) - - get :active - end - end - describe 'GET /logout' do it 'tracks a logout event' do stub_analytics @@ -139,41 +60,6 @@ end end - describe 'GET /timeout' do - it 'signs the user out' do - sign_in_as_user - - expect(subject.current_user).to_not be_nil - - get :timeout - - expect(flash[:info]).to eq t( - 'notices.session_timedout', - app_name: APP_NAME, - minutes: IdentityConfig.store.session_timeout_in_minutes, - ) - - expect(subject.current_user).to be_nil - end - - it 'redirects to the homepage' do - stub_sign_in - - get :timeout - - expect(response).to redirect_to(root_url) - end - - it 'tracks the timeout' do - stub_analytics - sign_in_as_user - - expect(@analytics).to receive(:track_event).with('Session Timed Out') - - get :timeout - end - end - describe 'POST /' do include AccountResetHelper @@ -671,77 +557,4 @@ end end end - - describe 'POST /sessions/keepalive' do - around do |ex| - freeze_time { ex.run } - end - - context 'when user is present' do - before do - stub_sign_in - end - - it 'returns a 200 status code' do - post :keepalive - - expect(response.status).to eq(200) - end - - it 'renders json' do - post :keepalive - - expect(response.media_type).to eq('application/json') - end - - it 'resets the timeout key' do - timeout = Time.zone.now + 2 - controller.session[:session_expires_at] = timeout - post :keepalive - - json ||= JSON.parse(response.body) - - expect(json['timeout'].to_datetime.to_i).to be >= timeout.to_i - expect(json['timeout'].to_datetime.to_i).to be_within(1).of( - Time.zone.now.to_i + IdentityConfig.store.session_timeout_in_minutes * 60, - ) - end - - it 'tracks session refresh visit' do - controller.session[:session_expires_at] = Time.zone.now + 10 - stub_analytics - - expect(@analytics).to receive(:track_event).with('Session Kept Alive') - - post :keepalive - end - end - - context 'when user is not present' do - it 'sets live key to false' do - post :keepalive - - json ||= JSON.parse(response.body) - - expect(json['live']).to eq false - end - - it 'includes session_expires_at' do - post :keepalive - - json ||= JSON.parse(response.body) - - expect(json['timeout'].to_datetime.to_i).to be_within(1).of(Time.zone.now.to_i - 1) - end - end - - it 'does not track analytics event' do - stub_sign_in - stub_analytics - - expect(@analytics).to_not receive(:track_event) - - get :active - end - end end diff --git a/spec/controllers/users/totp_setup_controller_spec.rb b/spec/controllers/users/totp_setup_controller_spec.rb index d9aabebb489..ad5e685a02d 100644 --- a/spec/controllers/users/totp_setup_controller_spec.rb +++ b/spec/controllers/users/totp_setup_controller_spec.rb @@ -42,7 +42,6 @@ user_signed_up: true, totp_secret_present: true, enabled_mfa_methods_count: 1, - sign_up_mfa_selection_order_bucket: nil, } expect(@analytics). @@ -78,7 +77,6 @@ user_signed_up: false, totp_secret_present: true, enabled_mfa_methods_count: 0, - sign_up_mfa_selection_order_bucket: nil, } expect(@analytics). @@ -117,7 +115,6 @@ auth_app_configuration_id: nil, enabled_mfa_methods_count: 0, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -155,7 +152,6 @@ auth_app_configuration_id: next_auth_app_id, enabled_mfa_methods_count: 2, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -194,7 +190,6 @@ auth_app_configuration_id: nil, enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -234,7 +229,6 @@ auth_app_configuration_id: nil, enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -273,7 +267,6 @@ auth_app_configuration_id: nil, enabled_mfa_methods_count: 0, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } expect(@analytics).to have_received(:track_event). @@ -312,7 +305,6 @@ auth_app_configuration_id: next_auth_app_id, enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_selection_order_bucket: :default, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -338,7 +330,6 @@ auth_app_configuration_id: next_auth_app_id, enabled_mfa_methods_count: 1, in_multi_mfa_selection_flow: true, - sign_up_mfa_selection_order_bucket: :default, pii_like_keypaths: [[:mfa_method_counts, :phone]], } @@ -375,7 +366,6 @@ auth_app_configuration_id: nil, enabled_mfa_methods_count: 0, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } diff --git a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb index 155130d97d1..f87a2031e2e 100644 --- a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb @@ -7,7 +7,7 @@ stub_analytics expect(@analytics).to receive(:track_event). - with('User Registration: 2FA Setup visited', sign_up_mfa_priority_bucket: :default) + with('User Registration: 2FA Setup visited') get :index end @@ -66,7 +66,7 @@ } params = ActionController::Parameters.new(voice_params) response = FormResponse.new(success: true, errors: {}, extra: { selection: ['voice'] }) - analytics_hash = response.to_h.merge(sign_up_mfa_priority_bucket: :default) + analytics_hash = response.to_h form_params = { user: user, phishing_resistant_required: false, piv_cac_required: nil } form = instance_double(TwoFactorOptionsForm) @@ -90,7 +90,6 @@ selection: ['voice', 'auth_app'], success: true, selected_mfa_count: 2, - sign_up_mfa_priority_bucket: :default, errors: {}, } diff --git a/spec/controllers/users/verify_password_controller_spec.rb b/spec/controllers/users/verify_password_controller_spec.rb index e8f91720325..33ec58f7c32 100644 --- a/spec/controllers/users/verify_password_controller_spec.rb +++ b/spec/controllers/users/verify_password_controller_spec.rb @@ -21,7 +21,7 @@ end context 'with password reset profile' do - let(:profiles) { [create(:profile, :password_reset)] } + let(:profiles) { [create(:profile, :verified, :password_reset)] } context 'without personal key flag set' do describe '#new' do diff --git a/spec/controllers/users/verify_personal_key_controller_spec.rb b/spec/controllers/users/verify_personal_key_controller_spec.rb index bcb30f58338..0262334b471 100644 --- a/spec/controllers/users/verify_personal_key_controller_spec.rb +++ b/spec/controllers/users/verify_personal_key_controller_spec.rb @@ -22,7 +22,7 @@ end context 'with password reset profile' do - let!(:profiles) { [create(:profile, :password_reset, user: user)] } + let!(:profiles) { [create(:profile, :verified, :password_reset, user: user)] } it 'renders the `new` template' do get :new @@ -46,7 +46,7 @@ end context 'with throttle reached' do - let!(:profiles) { [create(:profile, :password_reset, user: user)] } + let!(:profiles) { [create(:profile, :verified, :password_reset, user: user)] } before do Throttle.new(throttle_type: :verify_personal_key, user: user).increment_to_throttled! @@ -77,6 +77,7 @@ [ create( :profile, + :verified, :password_reset, user: user, pii: { ssn: '123456789' }, diff --git a/spec/controllers/users/webauthn_setup_controller_spec.rb b/spec/controllers/users/webauthn_setup_controller_spec.rb index 17337b540b0..e59ab7d82e1 100644 --- a/spec/controllers/users/webauthn_setup_controller_spec.rb +++ b/spec/controllers/users/webauthn_setup_controller_spec.rb @@ -54,7 +54,6 @@ errors: {}, enabled_mfa_methods_count: 0, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, success: true, ) @@ -90,7 +89,6 @@ success: true, errors: {}, in_multi_mfa_selection_flow: false, - sign_up_mfa_selection_order_bucket: nil, pii_like_keypaths: [[:mfa_method_counts, :phone]], } expect(@analytics).to receive(:track_event). @@ -220,7 +218,6 @@ enabled_mfa_methods_count: 1, errors: {}, in_multi_mfa_selection_flow: true, - sign_up_mfa_selection_order_bucket: :default, mfa_method_counts: { webauthn: 1 }, multi_factor_auth_method: 'webauthn', pii_like_keypaths: [[:mfa_method_counts, :phone]], @@ -278,7 +275,6 @@ enabled_mfa_methods_count: 1, errors: {}, in_multi_mfa_selection_flow: true, - sign_up_mfa_selection_order_bucket: :default, mfa_method_counts: { webauthn_platform: 1 }, multi_factor_auth_method: 'webauthn_platform', pii_like_keypaths: [[:mfa_method_counts, :phone]], @@ -330,7 +326,6 @@ link: MarketingSite.contact_url, )] }, in_multi_mfa_selection_flow: true, - sign_up_mfa_selection_order_bucket: :default, mfa_method_counts: {}, multi_factor_auth_method: 'webauthn_platform', pii_like_keypaths: [[:mfa_method_counts, :phone]], diff --git a/spec/factories/profiles.rb b/spec/factories/profiles.rb index 2e5dde2d9f5..12abf28abb1 100644 --- a/spec/factories/profiles.rb +++ b/spec/factories/profiles.rb @@ -13,11 +13,11 @@ trait :verified do verified_at { Time.zone.now } + activated_at { Time.zone.now } end trait :password_reset do - activated_at { Time.zone.now } - verified_at { Time.zone.now } + active { false } deactivation_reason { :password_reset } end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index afa8e08cadf..09eb35930bc 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -259,5 +259,15 @@ create(:profile, :password_reset, :with_pii, user: user) end end + + trait :suspended do + suspended_at { Time.zone.now } + reinstated_at { nil } + end + + trait :reinstated do + suspended_at { Time.zone.now } + reinstated_at { Time.zone.now + 1.hour } + end end end diff --git a/spec/features/idv/in_person_spec.rb b/spec/features/idv/in_person_spec.rb index 84447cffadc..352dd03e9d4 100644 --- a/spec/features/idv/in_person_spec.rb +++ b/spec/features/idv/in_person_spec.rb @@ -402,6 +402,11 @@ expect_in_person_gpo_step_indicator_current_step(t('step_indicator.flows.idv.get_a_letter')) click_button t('forms.verify_profile.submit') + # personal key + expect_in_person_step_indicator_current_step(t('step_indicator.flows.idv.secure_account')) + expect(page).to have_content(t('titles.idv.personal_key')) + acknowledge_and_confirm_personal_key + expect(page).to have_current_path(idv_in_person_ready_to_verify_path) expect_in_person_gpo_step_indicator_current_step( t('step_indicator.flows.idv.go_to_the_post_office'), diff --git a/spec/features/idv/steps/gpo_otp_verification_step_spec.rb b/spec/features/idv/steps/gpo_otp_verification_step_spec.rb index 989882f443a..fe52b3b71df 100644 --- a/spec/features/idv/steps/gpo_otp_verification_step_spec.rb +++ b/spec/features/idv/steps/gpo_otp_verification_step_spec.rb @@ -9,7 +9,15 @@ :profile, deactivation_reason: 3, gpo_verification_pending_at: 2.days.ago, - pii: { ssn: '123-45-6789', dob: '1970-01-01' }, + pii: { + address1: '1 Secure Way', + address2: 'Unit #4', + city: 'Loginville', + state: 'DC', + zipcode: '11111', + ssn: '123-45-6789', + dob: '1970-01-01', + }, fraud_review_pending_at: fraud_review_pending_timestamp, fraud_rejection_at: fraud_rejection_timestamp, ) @@ -111,4 +119,34 @@ expect(page).to_not have_content(t('account.index.verification.reactivate_button')) end end + + it 'allows a user to cancel and start over withinthe banner' do + sign_in_live_with_2fa(user) + + expect(current_path).to eq idv_gpo_verify_path + expect(page).to have_content t('forms.verify_profile.alert_info') + expect(page).to have_content t('forms.verify_profile.wrong_address') + expect(page).to have_content '1 Secure Way' + + click_on t('forms.verify_profile.clear_and_start_over') + + expect(current_path).to eq idv_confirm_start_over_path + + click_idv_continue + + expect(current_path).to eq idv_doc_auth_welcome_step + end + + it 'allows a user to cancel and start over in the footer' do + sign_in_live_with_2fa(user) + + expect(current_path).to eq idv_gpo_verify_path + click_on t('idv.messages.clear_and_start_over') + + expect(current_path).to eq idv_confirm_start_over_path + + click_idv_continue + + expect(current_path).to eq idv_doc_auth_welcome_step + end end diff --git a/spec/features/idv/steps/review_step_spec.rb b/spec/features/idv/steps/review_step_spec.rb index 99a0ff02841..7f8c11d543f 100644 --- a/spec/features/idv/steps/review_step_spec.rb +++ b/spec/features/idv/steps/review_step_spec.rb @@ -59,12 +59,17 @@ complete_idv_steps_with_gpo_before_review_step(user) end - it 'sends a letter and creates an unverified profile' do + it 'sends a letter, creates an unverified profile, and sends an email' do fill_in 'Password', with: user_password + email_count_before_continue = ActionMailer::Base.deliveries.count + expect { click_continue }. to change { GpoConfirmation.count }.from(0).to(1) + expect_delivered_email_count(email_count_before_continue + 1) + expect(last_email.subject).to eq(t('user_mailer.letter_reminder.subject')) + expect(user.events.account_verified.size).to be(0) expect(user.profiles.count).to eq 1 diff --git a/spec/forms/verify_password_form_spec.rb b/spec/forms/verify_password_form_spec.rb index 6d631beeefc..0576b92a890 100644 --- a/spec/forms/verify_password_form_spec.rb +++ b/spec/forms/verify_password_form_spec.rb @@ -7,7 +7,7 @@ password = 'cab123DZN456' user = create(:user, password: password) pii = { ssn: '111111111' } - create(:profile, :password_reset, user: user, pii: pii) + create(:profile, :verified, :password_reset, user: user, pii: pii) form = VerifyPasswordForm.new( user: user, password: password, @@ -25,7 +25,7 @@ password = 'cab123DZN456' user = create(:user, password: password) pii = { ssn: '111111111' } - create(:profile, :password_reset, user: user, pii: pii) + create(:profile, :verified, :password_reset, user: user, pii: pii) form = VerifyPasswordForm.new( user: user, password: "#{password}a", diff --git a/spec/forms/verify_personal_key_form_spec.rb b/spec/forms/verify_personal_key_form_spec.rb index d741bf016d6..36be3ece540 100644 --- a/spec/forms/verify_personal_key_form_spec.rb +++ b/spec/forms/verify_personal_key_form_spec.rb @@ -4,7 +4,7 @@ let(:user) { create(:user) } let!(:profile) do - create(:profile, :active, :password_reset, user: user, pii: { ssn: '123456789' }) + create(:profile, :verified, :password_reset, user: user, pii: { ssn: '123456789' }) end subject(:form) do diff --git a/spec/helpers/session_timeout_warning_helper_spec.rb b/spec/helpers/session_timeout_warning_helper_spec.rb index f6ebec301f7..ca129f1c895 100644 --- a/spec/helpers/session_timeout_warning_helper_spec.rb +++ b/spec/helpers/session_timeout_warning_helper_spec.rb @@ -1,26 +1,6 @@ require 'rails_helper' RSpec.describe SessionTimeoutWarningHelper do - describe '#expires_at' do - around do |ex| - freeze_time { ex.run } - end - - it 'returns time before now' do - expect(helper.expires_at).to be < Time.zone.now - end - - context 'with session expiration' do - before do - allow(helper).to receive(:session).and_return(session_expires_at: Time.zone.now + 1) - end - - it 'returns time remaining in user session' do - expect(helper.expires_at).to be > Time.zone.now - end - end - end - describe '#timeout_refresh_path' do let(:http_host) { 'example.com' } before do diff --git a/spec/mailers/previews/report_mailer_preview.rb b/spec/mailers/previews/report_mailer_preview.rb deleted file mode 100644 index f3c71460244..00000000000 --- a/spec/mailers/previews/report_mailer_preview.rb +++ /dev/null @@ -1,10 +0,0 @@ -class ReportMailerPreview < ActionMailer::Preview - def warn_error - ReportMailer.warn_error( - email: 'test@example.com', - error: ServiceProviderSeeder::ExtraServiceProviderError.new( - 'Extra service providers found in DB: a, b, c', - ), - ) - end -end diff --git a/spec/mailers/previews/report_mailer_preview_spec.rb b/spec/mailers/previews/report_mailer_preview_spec.rb deleted file mode 100644 index fb1fc38697f..00000000000 --- a/spec/mailers/previews/report_mailer_preview_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'rails_helper' -require_relative './report_mailer_preview' - -RSpec.describe ReportMailerPreview do - subject(:mailer_preview) { ReportMailerPreview.new } - - describe '#warn_error' do - it 'generates a warn_error email' do - expect { mailer_preview.warn_error }.to_not raise_error - end - end -end diff --git a/spec/mailers/report_mailer_spec.rb b/spec/mailers/report_mailer_spec.rb index 9a7f076c638..51710c91bcb 100644 --- a/spec/mailers/report_mailer_spec.rb +++ b/spec/mailers/report_mailer_spec.rb @@ -4,7 +4,7 @@ let(:user) { build(:user) } let(:email_address) { user.email_addresses.first } - describe '#deleted_user_accounts_report' do + describe 'deleted_user_accounts_report' do let(:mail) do ReportMailer.deleted_user_accounts_report( email: email_address.email, @@ -30,26 +30,4 @@ expect(mail.html_part.body).to have_content('issuer2') end end - - describe '#warn_error' do - let(:error) { RuntimeError.new('this is my test message') } - let(:env) { ActiveSupport::StringInquirer.new('prod') } - - let(:mail) do - ReportMailer.warn_error( - email: 'test@example.com', - error: error, - env: env, - ) - end - - it 'puts the rails env and error in a plaintext email', aggregate_failures: true do - expect(mail.html_part).to be_nil - - expect(mail.subject).to include('prod') - expect(mail.subject).to include('RuntimeError') - - expect(mail.text_part.body).to include('this is my test') - end - end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b2f19cd00b2..bdf7ba1709c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -558,6 +558,7 @@ user = User.new create( :profile, + :verified, :password_reset, created_at: 1.day.ago, user: user, @@ -675,10 +676,10 @@ end context 'the user has no active profile but has a previously verified profile' do - let!(:password_reset_profile) do + let!(:verified_profile) do create( :profile, - :password_reset, + :verified, user: user, ) end @@ -694,7 +695,133 @@ it 'returns the date of the previously verified profile' do expect( user.personal_key_generated_at, - ).to be_within(1.second).of(password_reset_profile.verified_at) + ).to be_within(1.second).of(verified_profile.verified_at) + end + end + end + + describe 'user suspension' do + let(:user) { User.new } + let(:cannot_reinstate_message) { :user_is_not_suspended } + let(:cannot_suspend_message) { :user_already_suspended } + + describe '#suspended?' do + context 'when suspended_at is after reinstated_at' do + before do + user.suspended_at = Time.zone.now + user.reinstated_at = Time.zone.now - 1.day + end + it 'returns true' do + expect(user.suspended?).to be true + end + end + + context 'when suspended_at is before reinstated_at' do + before do + user.suspended_at = Time.zone.now - 1.day + user.reinstated_at = Time.zone.now + end + + it 'returns false' do + expect(user.suspended?).to be false + end + end + + context 'when suspended_at is nil' do + before do + user.suspended_at = nil + user.reinstated_at = nil + end + + it 'returns false' do + expect(user.suspended?).to be false + end + end + end + + describe '#reinstated?' do + context 'when reinstated_at is after suspended_at' do + before do + user.suspended_at = Time.zone.now - 1.day + user.reinstated_at = Time.zone.now + end + + it 'returns true' do + expect(user.reinstated?).to be true + end + end + + context 'when reinstated_at is before suspended_at' do + before do + user.suspended_at = Time.zone.now + user.reinstated_at = Time.zone.now - 1.day + end + + it 'returns false' do + expect(user.reinstated?).to be false + end + end + + context 'when reinstated_at is nil' do + before do + user.suspended_at = nil + user.reinstated_at = nil + end + it 'returns false' do + expect(user.reinstated?).to be false + end + end + end + + describe '#suspend!' do + it 'updates the suspended_at attribute with the current time' do + expect do + user.suspend! + end.to change(user, :suspended_at).from(nil).to(be_within(1.second).of(Time.zone.now)) + end + + it 'tracks the user suspension' do + expect(user.analytics).to receive(:user_suspended).with(success: true) + user.suspend! + end + + it 'raises an error if the user is already suspended' do + user.suspended_at = Time.zone.now + expect(user.analytics).to receive(:user_suspended).with( + success: false, + error_message: cannot_suspend_message, + ) + expect do + user.suspend! + end.to raise_error(cannot_suspend_message.to_s) + end + end + + describe '#reinstate!' do + before do + user.suspended_at = Time.zone.now + user.reinstated_at = nil + end + it 'updates the reinstated_at attribute with the current time' do + expect do + user.reinstate! + end.to change(user, :reinstated_at).from(nil).to(be_within(1.second).of(Time.zone.now)) + end + + it 'tracks the user reinstatement' do + expect(user.analytics).to receive(:user_reinstated).with(success: true) + user.reinstate! + end + + it 'raises an error if the user is not currently suspended' do + user.suspended_at = nil + expect(user.analytics).to receive(:user_reinstated).with( + success: false, + error_message: cannot_reinstate_message, + ) + expect do + user.reinstate! + end.to raise_error(cannot_reinstate_message.to_s) end end end diff --git a/spec/presenters/idv/gpo_presenter_spec.rb b/spec/presenters/idv/gpo_presenter_spec.rb index dba1de3f88e..6a36c0358c2 100644 --- a/spec/presenters/idv/gpo_presenter_spec.rb +++ b/spec/presenters/idv/gpo_presenter_spec.rb @@ -67,7 +67,7 @@ context 'when the user has a pending profile' do it 'returns the verify account path' do create(:profile, user: user, gpo_verification_pending_at: 1.day.ago) - expect(subject.fallback_back_path).to eq('/account/verify') + expect(subject.fallback_back_path).to eq('/verify/by_mail') end end diff --git a/spec/presenters/two_factor_options_presenter_spec.rb b/spec/presenters/two_factor_options_presenter_spec.rb index c0f5c9fe90f..4e561489f94 100644 --- a/spec/presenters/two_factor_options_presenter_spec.rb +++ b/spec/presenters/two_factor_options_presenter_spec.rb @@ -21,11 +21,11 @@ describe '#options' do it 'supplies all the options for a user' do expect(presenter.options.map(&:class)).to eq [ - TwoFactorAuthentication::WebauthnSelectionPresenter, - TwoFactorAuthentication::PivCacSelectionPresenter, TwoFactorAuthentication::AuthAppSelectionPresenter, TwoFactorAuthentication::PhoneSelectionPresenter, TwoFactorAuthentication::BackupCodeSelectionPresenter, + TwoFactorAuthentication::WebauthnSelectionPresenter, + TwoFactorAuthentication::PivCacSelectionPresenter, ] end @@ -52,10 +52,10 @@ it 'supplies all the options except phone' do expect(presenter.options.map(&:class)).to eq [ - TwoFactorAuthentication::WebauthnSelectionPresenter, - TwoFactorAuthentication::PivCacSelectionPresenter, TwoFactorAuthentication::AuthAppSelectionPresenter, TwoFactorAuthentication::BackupCodeSelectionPresenter, + TwoFactorAuthentication::WebauthnSelectionPresenter, + TwoFactorAuthentication::PivCacSelectionPresenter, ] end end @@ -67,37 +67,14 @@ it 'supplies all the options except webauthn' do expect(presenter.options.map(&:class)).to eq [ - TwoFactorAuthentication::WebauthnPlatformSelectionPresenter, - TwoFactorAuthentication::WebauthnSelectionPresenter, - TwoFactorAuthentication::PivCacSelectionPresenter, TwoFactorAuthentication::AuthAppSelectionPresenter, TwoFactorAuthentication::PhoneSelectionPresenter, + TwoFactorAuthentication::WebauthnPlatformSelectionPresenter, TwoFactorAuthentication::BackupCodeSelectionPresenter, + TwoFactorAuthentication::WebauthnSelectionPresenter, + TwoFactorAuthentication::PivCacSelectionPresenter, ] end end - context 'when priority is authentication first in a/b testing bucket' do - let(:presenter) do - described_class.new(user_agent: user_agent, priority_bucket: :authentication_app_priority) - end - - it 'supplies auth app as the first option' do - expect(presenter.options.first.class).to eq( - TwoFactorAuthentication::AuthAppSelectionPresenter, - ) - end - end - - context 'when priority is usability ordered in a/b testing bucket' do - let(:presenter) do - described_class.new(user_agent: user_agent, priority_bucket: :usability_priority) - end - - it 'supplies phone as the first option' do - expect(presenter.options.first.class).to eq( - TwoFactorAuthentication::PhoneSelectionPresenter, - ) - end - end end end diff --git a/spec/requests/redirects_spec.rb b/spec/requests/redirects_spec.rb index 7f0c11a47d0..f89d4996334 100644 --- a/spec/requests/redirects_spec.rb +++ b/spec/requests/redirects_spec.rb @@ -24,4 +24,20 @@ expect(response).to redirect_to('/account/verify') end end + + describe '/account/verify' do + it 'redirects to /verify/by_mail' do + get '/account/verify' + + expect(response).to redirect_to('/verify/by_mail') + end + end + + describe '/account/verify/confirm_start_over' do + it 'redirects to /verify/by_mail/confirm_start_over' do + get '/account/verify/confirm_start_over' + + expect(response).to redirect_to('/verify/by_mail/confirm_start_over') + end + end end diff --git a/spec/services/idv/profile_maker_spec.rb b/spec/services/idv/profile_maker_spec.rb index 0a58713e2f6..67ed726a9ea 100644 --- a/spec/services/idv/profile_maker_spec.rb +++ b/spec/services/idv/profile_maker_spec.rb @@ -19,7 +19,6 @@ it 'creates an inactive Profile with encrypted PII' do proofing_component = ProofingComponent.create(user_id: user.id, document_check: 'acuant') profile = subject.save_profile( - active: false, fraud_review_needed: false, gpo_verification_needed: false, ) @@ -41,7 +40,6 @@ context 'with deactivation reason' do it 'creates an inactive profile with deactivation reason' do profile = subject.save_profile( - active: false, fraud_review_needed: false, gpo_verification_needed: false, deactivation_reason: :encryption_error, @@ -55,7 +53,6 @@ context 'with fraud review needed' do it 'deactivates a profile for fraud review' do profile = subject.save_profile( - active: false, fraud_review_needed: true, gpo_verification_needed: false, deactivation_reason: nil, @@ -69,7 +66,6 @@ context 'with gpo_verification_needed' do it 'deactivates a profile for gpo verification' do profile = subject.save_profile( - active: false, fraud_review_needed: false, gpo_verification_needed: true, deactivation_reason: nil, @@ -83,13 +79,12 @@ context 'as active' do it 'creates an active profile' do profile = subject.save_profile( - active: true, fraud_review_needed: false, gpo_verification_needed: false, deactivation_reason: nil, ) - expect(profile.active).to eq true + expect(profile.active).to eq false expect(profile.deactivation_reason).to be_nil end end @@ -99,7 +94,6 @@ it 'creates a profile with the initiating sp recorded' do profile = subject.save_profile( - active: true, fraud_review_needed: false, gpo_verification_needed: false, deactivation_reason: nil, diff --git a/spec/services/service_provider_seeder_spec.rb b/spec/services/service_provider_seeder_spec.rb index 3151b056fd9..ab1e7852f16 100644 --- a/spec/services/service_provider_seeder_spec.rb +++ b/spec/services/service_provider_seeder_spec.rb @@ -83,10 +83,6 @@ let(:prod_issuer) { 'urn:gov:login:test-providers:fake-prod-sp' } let(:unrestricted_issuer) { 'urn:gov:login:test-providers:fake-unrestricted-sp' } - before do - allow(IdentityConfig.store).to receive(:team_ursula_email).and_return('team@example.com') - end - context 'when %{env} is present in the config file' do let(:deploy_env) { 'dev' } @@ -110,10 +106,12 @@ expect(ServiceProvider.find_by(issuer: unrestricted_issuer)).not_to be_present end - it 'sends an email an error if the DB has an SP not in the config' do + it 'sends New Relic an error if the DB has an SP not in the config' do + allow(NewRelic::Agent).to receive(:notice_error) create(:service_provider, issuer: 'missing_issuer') + run - expect { run }.to change { ActionMailer::Base.deliveries.count }.by(1) + expect(NewRelic::Agent).to have_received(:notice_error) end end @@ -130,9 +128,11 @@ end it 'sends New Relic an error if the DB has an SP not in the config' do + allow(NewRelic::Agent).to receive(:notice_error) create(:service_provider, issuer: 'missing_issuer') + run - expect { run }.to change { ActionMailer::Base.deliveries.count }.by(1) + expect(NewRelic::Agent).to have_received(:notice_error) end end diff --git a/spec/support/idv_examples/gpo_otp_verification.rb b/spec/support/idv_examples/gpo_otp_verification.rb index 176aadadff2..809f604d135 100644 --- a/spec/support/idv_examples/gpo_otp_verification.rb +++ b/spec/support/idv_examples/gpo_otp_verification.rb @@ -16,13 +16,13 @@ if profile_should_be_active expect(profile.active).to be(true) expect(profile.deactivation_reason).to be(nil) + expect(user.events.account_verified.size).to eq 1 else expect(profile.active).to be(false) expect(profile.fraud_review_pending?).to eq(fraud_review_pending) if fraud_review_pending expect(profile.deactivation_reason).to eq(nil) if fraud_review_pending end - expect(user.events.account_verified.size).to eq 1 expect(page).to_not have_content(t('account.index.verification.reactivate_button')) end