Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion app/controllers/concerns/idv/choose_id_type_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@ def set_passport_requested
if chosen_id_type == 'passport'
document_capture_session.update!(passport_status: 'requested')
else
document_capture_session.update!(passport_status: 'allowed')
document_capture_session.update!(passport_status: 'not_requested')
end
end

def choose_id_type_form_params
params.require(:doc_auth).permit(:choose_id_type_preference)
end

def auto_check_value
case document_capture_session.passport_status
when 'requested'
:passport
when 'not_requested'
:drivers_license
end
end
end
end
5 changes: 0 additions & 5 deletions app/controllers/concerns/remember_device_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ def mfa_expiration_interval
aal_1_expiration = IdentityConfig.store.remember_device_expiration_hours_aal_1.hours
aal_2_expiration = IdentityConfig.store.remember_device_expiration_minutes_aal_2.minutes

return aal_2_expiration if sp_aal > 1
return aal_2_expiration if sp_ial > 1
return aal_2_expiration if resolved_authn_context_result&.aal2?

Expand All @@ -66,10 +65,6 @@ def mfa_expiration_interval

private

def sp_aal
current_sp&.default_aal || 1
end

def sp_ial
current_sp&.ial || 1
end
Expand Down
20 changes: 14 additions & 6 deletions app/controllers/concerns/two_factor_authenticatable_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,17 @@ def authenticate_user
authenticate_user!(force: true)
end

def handle_second_factor_locked_user(type:)
def handle_second_factor_locked_user(type:, context: nil)
analytics.multi_factor_auth_max_attempts

if context
if UserSessionContext.confirmation_context?(context)
attempts_api_tracker.mfa_enroll_code_rate_limited(mfa_device_type: type)
elsif UserSessionContext.authentication_context?(context)
attempts_api_tracker.mfa_submission_code_rate_limited(mfa_device_type: type)
end
end

event = PushNotification::MfaLimitAccountLockedEvent.new(user: current_user)
PushNotification::HttpPush.deliver(event)
handle_max_attempts(type + '_login_attempts')
Expand Down Expand Up @@ -150,22 +159,21 @@ def mfa_attempts_count
user_session.dig(:mfa_attempts, :attempts)
end

# Method will be renamed in the next refactor.
# You can pass in any "type" with a corresponding I18n key in
# two_factor_authentication.invalid_#{type}
def handle_invalid_otp(type:)
def handle_invalid_mfa(type:, context:)
update_invalid_user

flash.now[:error] = invalid_otp_error(type)
flash.now[:error] = invalid_error(type)

if current_user.locked_out?
handle_second_factor_locked_user(type:)
handle_second_factor_locked_user(type:, context:)
else
render_show_after_invalid
end
end

def invalid_otp_error(type)
def invalid_error(type)
case type
when 'otp'
[t('two_factor_authentication.invalid_otp'),
Expand Down
4 changes: 3 additions & 1 deletion app/controllers/idv/choose_id_type_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class ChooseIdTypeController < ApplicationController
def show
analytics.idv_doc_auth_choose_id_type_visited(**analytics_arguments)
render 'idv/shared/choose_id_type',
locals: { form_url: idv_choose_id_type_path, is_hybrid: false },
locals: { form_url: idv_choose_id_type_path,
is_hybrid: false,
auto_check_value: auto_check_value },
layout: true
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ class ChooseIdTypeController < ApplicationController
def show
analytics.idv_doc_auth_choose_id_type_visited(**analytics_arguments)
render 'idv/shared/choose_id_type',
locals: { form_url: idv_hybrid_mobile_choose_id_type_path, is_hybrid: true }
locals: { form_url: idv_hybrid_mobile_choose_id_type_path,
is_hybrid: true,
auto_check_value: auto_check_value }
end

def update
Expand Down
42 changes: 34 additions & 8 deletions app/controllers/test/ipp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ module Test
class IppController < ApplicationController
layout 'no_card'

before_action :render_not_found_in_production
before_action :authorize
before_action :confirm_two_factor_authenticated

def index
@enrollments = InPersonEnrollment
.order(created_at: :desc)
.limit(10)
@enrollments = Rails.env.development? ? all_enrollments : enrollments_for_current_user

@enrollments_with_actions = @enrollments.map do |e|
case e.status
Expand All @@ -20,18 +19,44 @@ def index
end

def update
enrollment_id = params['enrollment'].to_i
enrollment = InPersonEnrollment.find(enrollment_id)
enrollment = Rails.env.development? ? enrollment_for_id : enrollment_for_current_user

if enrollment.present?
approve_enrollment(enrollment)
else
flash[:error] = "Could not find pending IPP enrollment with ID #{enrollment_id}"
end

redirect_to test_ipp_url
end

private

def enrollments_for_current_user
InPersonEnrollment
.order(created_at: :desc)
.where(user_id: current_user&.id)
end

def all_enrollments
InPersonEnrollment
.includes(:user)
.order(created_at: :desc)
.limit(10)
end

def enrollment_for_current_user
InPersonEnrollment.find_by(id: enrollment_id, user_id: current_user&.id)
end

def enrollment_for_id
InPersonEnrollment.find_by(id: enrollment_id)
end

def enrollment_id
params['enrollment'].to_i
end

def approve_enrollment(enrollment)
return if !enrollment.pending?

Expand All @@ -52,8 +77,9 @@ def approve_enrollment(enrollment)
job.send(:process_enrollment_response, enrollment, res)
end

def render_not_found_in_production
return unless Rails.env.production?
def authorize
return if FeatureManagement.allow_ipp_enrollment_approval?

render_not_found
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def handle_invalid_backup_code(result)
flash.now[:error] = result.first_error_message

if current_user.locked_out?
handle_second_factor_locked_user(type: 'backup_code')
handle_second_factor_locked_user(type: 'backup_code', context:)
else
render_show_after_invalid
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def create
reset_otp_session_data
user_session.delete(:mfa_attempts)
else
handle_invalid_otp(type: 'otp')
handle_invalid_mfa(type: 'otp', context:)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def handle_result(result)

handle_valid_otp
else
handle_invalid_otp(type: 'personal_key')
handle_invalid_mfa(type: 'personal_key', context:)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def handle_invalid_piv_cac
update_invalid_user

if current_user.locked_out?
handle_second_factor_locked_user(type: 'piv_cac')
handle_second_factor_locked_user(type: 'piv_cac', context:)
elsif redirect_for_piv_cac_mismatch_replacement?
redirect_to login_two_factor_piv_cac_mismatch_url
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def create
handle_remember_device_preference(params[:remember_device])
redirect_to after_sign_in_path_for(current_user)
else
handle_invalid_otp(type: 'totp')
handle_invalid_mfa(type: 'totp', context:)
end
end

Expand Down
2 changes: 2 additions & 0 deletions app/controllers/users/delete_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def delete
notify_user_via_email_of_deletion
notify_user_via_sms_of_deletion
analytics.account_delete_submitted(success: true)
attempts_api_tracker.logged_in_account_purged(success: true)
delete_user
sign_out
flash[:success] = t('devise.registrations.destroyed')
Expand All @@ -37,6 +38,7 @@ def confirm_current_password

flash.now[:error] = t('idv.errors.incorrect_password')
analytics.account_delete_submitted(success: false)
attempts_api_tracker.logged_in_account_purged(success: false)
render :show
end

Expand Down
4 changes: 4 additions & 0 deletions app/controllers/users/passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def update
)

result = @update_user_password_form.submit(user_password_params)
attempts_api_tracker.logged_in_password_change(
success: result.success?,
failure_reason: attempts_api_tracker.parse_failure_reason(result),
)

analytics.password_changed(**result)

Expand Down
12 changes: 12 additions & 0 deletions app/controllers/users/webauthn_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ def new
errors: result.errors,
success: false,
)

if @platform_authenticator
attempts_api_tracker.mfa_enroll_webauthn_platform(success: false)
else
attempts_api_tracker.mfa_enroll_webauthn_roaming(success: false)
end
end

flash_error(result.errors) unless result.success?
Expand All @@ -74,6 +80,12 @@ def confirm
)
properties = result.to_h.merge(analytics_properties)
analytics.multi_factor_auth_setup(**properties)
if @platform_authenticator
attempts_api_tracker.mfa_enroll_webauthn_platform(success: result.success?)
else
attempts_api_tracker.mfa_enroll_webauthn_roaming(success: result.success?)
end

if result.success?
process_valid_webauthn(form)
user_session.delete(:mfa_attempts)
Expand Down
7 changes: 5 additions & 2 deletions app/jobs/get_usps_proofing_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def check_enrollment(enrollment)
handle_standard_error(err, enrollment)
else
if profile_deactivation_reason == 'password_reset'
skip_enrollment(enrollment, profile_deactivation_reason)
skip_enrollment(enrollment, profile_deactivation_reason, response)
else
process_enrollment_response(enrollment, response)
end
Expand All @@ -146,12 +146,14 @@ def cancel_enrollment(enrollment)
enrollment.cancel
end

def skip_enrollment(enrollment, profile_deactivation_reason)
def skip_enrollment(enrollment, profile_deactivation_reason, response)
analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_skipped(
**enrollment_analytics_attributes(enrollment, complete: false),
**response_analytics_attributes(response),
reason: "Profile has a deactivation reason of #{profile_deactivation_reason}",
job_name: self.class.name,
)
enrollment.update(status_check_completed_at: Time.zone.now)
enrollment_outcomes[:enrollments_skipped] += 1
end

Expand Down Expand Up @@ -679,6 +681,7 @@ def enrollment_analytics_attributes(enrollment, complete:)

def response_analytics_attributes(response)
return { response_present: false } unless response.present?
return {} unless response.is_a?(Hash)

{
fraud_suspected: response['fraudSuspected'],
Expand Down
1 change: 1 addition & 0 deletions app/models/document_capture_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class DocumentCaptureSession < ApplicationRecord

PASSPORT_STATUSES = [
'allowed',
'not_requested',
'requested',
].freeze

Expand Down
1 change: 0 additions & 1 deletion app/models/phone_configuration.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

class PhoneConfiguration < ApplicationRecord
self.ignored_columns += %w[confirmation_sent_at]
include EncryptableAttribute

belongs_to :user, inverse_of: :phone_configurations
Expand Down
1 change: 0 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

class User < ApplicationRecord
self.ignored_columns += %w[phone_confirmed_at]
include NonNullUuid

include ActionView::Helpers::DateHelper
Expand Down
Loading