diff --git a/app/forms/gpo_verify_form.rb b/app/forms/gpo_verify_form.rb index 891ee80c756..3c9261d92df 100644 --- a/app/forms/gpo_verify_form.rb +++ b/app/forms/gpo_verify_form.rb @@ -24,7 +24,7 @@ def submit UspsInPersonProofing::EnrollmentHelper.schedule_in_person_enrollment(user, pii) pending_profile&.deactivate(:in_person_verification_pending) elsif fraud_review_checker.fraud_check_failed? && threatmetrix_enabled? - bump_fraud_review_pending_timestamps + pending_profile&.deactivate_for_fraud_review elsif fraud_review_checker.fraud_check_failed? pending_profile&.activate_after_fraud_review_unnecessary else @@ -57,10 +57,6 @@ def gpo_confirmation_code pending_profile.gpo_confirmation_codes.first_with_otp(otp) end - def bump_fraud_review_pending_timestamps - pending_profile&.bump_fraud_review_pending_timestamps - end - def validate_otp_not_expired return unless gpo_confirmation_code.present? && gpo_confirmation_code.expired? diff --git a/app/models/profile.rb b/app/models/profile.rb index c446d7f7d54..66f9f3f503b 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -144,10 +144,29 @@ def deactivate_for_gpo_verification update!(active: false, gpo_verification_pending_at: Time.zone.now) end - def deactivate_for_fraud_review(fraud_pending_reason:) + def deactivate_for_fraud_review + ## + # This is temporary. We are working on changing the way fraud review status + # is computed. The goal is that a profile is only in fraud review when + # `fraud_review_pending_at` is set. We will set this immediatly if a user + # verifies with phone and when a user enters their GPO code. + # + # We currently look at `fraud_pending_reason` to determine if a user is in + # fraud review. This allows us to change the writes on + # `fraud_review_pending_at` without side-effects. + # + # Once the writes on `fraud_review_pending_at` are correct we can move the + # reads to determine a user is fraud review pending to that column. At that + # point we can set `fraud_pending_reason` when we create a profile and + # deactivate the profile at the appropriate time for the given context + # (i.e. immediatly for phone and after GPO code entry for GPO). + # + if fraud_pending_reason.nil? + raise 'Attempting to deactivate a profile with a nil fraud pending reason' + end + update!( active: false, - fraud_pending_reason: fraud_pending_reason, fraud_review_pending_at: Time.zone.now, fraud_rejection_at: nil, ) diff --git a/app/services/idv/profile_maker.rb b/app/services/idv/profile_maker.rb index 8607bd1fecd..c846e8d4b30 100644 --- a/app/services/idv/profile_maker.rb +++ b/app/services/idv/profile_maker.rb @@ -18,10 +18,11 @@ def save_profile( profile.initiating_service_provider = initiating_service_provider profile.encrypt_pii(pii_attributes, user_password) profile.proofing_components = current_proofing_components + profile.fraud_pending_reason = fraud_pending_reason profile.save! profile.deactivate_for_gpo_verification if gpo_verification_needed - if fraud_pending_reason.present? - profile.deactivate_for_fraud_review(fraud_pending_reason: fraud_pending_reason) + if fraud_pending_reason.present? && !gpo_verification_needed + profile.deactivate_for_fraud_review end profile end diff --git a/lib/tasks/backfill_fraud_review_pending_at.rake b/lib/tasks/backfill_fraud_review_pending_at.rake new file mode 100644 index 00000000000..9b250fbd83c --- /dev/null +++ b/lib/tasks/backfill_fraud_review_pending_at.rake @@ -0,0 +1,75 @@ +namespace :profiles do + desc 'If a profile is in GPO and fraud pending state, move it out of fraud pending state' + + ## + # Usage: + # + # Print pending updates + # bundle exec rake profiles:backfill_fraud_review_pending_at + # + # Commit updates + # bundle exec rake profiles:backfill_fraud_review_pending_at UPDATE_PROFILES=true + # + task backfill_fraud_review_pending_at: :environment do |_task, _args| + ActiveRecord::Base.connection.execute('SET statement_timeout = 60000') + + update_profiles = ENV['UPDATE_PROFILES'] == 'true' + + profiles = Profile.where( + 'fraud_review_pending_at IS NOT NULL OR fraud_rejection_at IS NOT NULL', + ).where.not( + gpo_verification_pending_at: nil, + ) + + profiles.each do |profile| + if profile.fraud_pending_reason.blank? + warn "Profile ##{profile.id} does not have a fraud pending reason!" + break + end + + warn "#{profile.id},#{profile.fraud_review_pending_at},#{profile.fraud_rejection_at}" + profile.update!(fraud_review_pending_at: nil, fraud_rejection_at: nil) if update_profiles + end + end + + ## + # Usage: + # + # Rollback the above: + # + # export BACKFILL_OUTPUT='' + # bundle exec rake profiles:rollback_backfill_fraud_review_pending_at + # + task rollback_backfill_fraud_review_pending_at: :environment do |_task, _args| + ActiveRecord::Base.connection.execute('SET statement_timeout = 60000') + + profile_data = ENV['BACKFILL_OUTPUT'].split("\n").map do |profile_row| + profile_row.split(',') + end + + warn "Updating #{profile_data.count} records" + profile_data.each do |profile_datum| + profile_id, fraud_review_pending_at, fraud_rejection_at = profile_datum + Profile.where(id: profile_id).update!( + fraud_review_pending_at: fraud_review_pending_at, + fraud_rejection_at: fraud_rejection_at, + ) + end + end + + ## + # Usage: + # bundle exec rake profiles:validate_backfill_fraud_review_pending_at + # + task validate_backfill_fraud_review_pending_at: :environment do |_task, _args| + ActiveRecord::Base.connection.execute('SET statement_timeout = 60000') + + profiles = Profile.where( + 'fraud_review_pending_at IS NOT NULL OR fraud_rejection_at IS NOT NULL', + ).where.not( + gpo_verification_pending_at: nil, + ) + + warn "fraud_pending_reason backfill left #{profiles.count} rows" + end +end diff --git a/spec/controllers/idv/personal_key_controller_spec.rb b/spec/controllers/idv/personal_key_controller_spec.rb index 27b7393791c..169e8e3e126 100644 --- a/spec/controllers/idv/personal_key_controller_spec.rb +++ b/spec/controllers/idv/personal_key_controller_spec.rb @@ -90,7 +90,8 @@ def index context 'profile is pending from a different session' do context 'profile is pending due to fraud review' do before do - profile.deactivate_for_fraud_review(fraud_pending_reason: 'threatmetrix_review') + profile.fraud_pending_reason = 'threatmetrix_review' + profile.deactivate_for_fraud_review subject.idv_session.profile_id = nil end @@ -290,7 +291,8 @@ def index context 'profile is in fraud_review' do before do - profile.deactivate_for_fraud_review(fraud_pending_reason: 'threatmetrix_review') + profile.fraud_pending_reason = 'threatmetrix_review' + profile.deactivate_for_fraud_review end it 'redirects to idv please call path' do diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb index cd023aefc8d..afb877f08dc 100644 --- a/spec/models/profile_spec.rb +++ b/spec/models/profile_spec.rb @@ -979,7 +979,8 @@ expect(profile.initiating_service_provider).to be_nil expect(profile.verified_at).to be_nil - profile.deactivate_for_fraud_review(fraud_pending_reason: 'threatmetrix_review') + profile.fraud_pending_reason = 'threatmetrix_review' + profile.deactivate_for_fraud_review expect(profile.activated_at).to be_nil expect(profile.active).to eq(false)