Skip to content

Commit

Permalink
Merge pull request #11838 from 18F/stages/rc-2025-02-04
Browse files Browse the repository at this point in the history
Deploy RC 449 to Production
  • Loading branch information
amirbey authored Feb 4, 2025
2 parents 891dec3 + be5c722 commit ec142ef
Show file tree
Hide file tree
Showing 97 changed files with 1,989 additions and 1,068 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def edit
@select_email_form = build_select_email_form
@can_add_email = EmailPolicy.new(current_user).can_add_email?
analytics.sp_select_email_visited
@email_id = @identity.email_address_id || last_email
@email_id = @identity.email_address_id || last_email_id
end

def update
Expand Down Expand Up @@ -52,7 +52,7 @@ def identity
@identity = current_user.identities.find_by(id: params[:identity_id])
end

def last_email
def last_email_id
current_user.last_sign_in_email_address.id
end
end
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/concerns/saml_idp_auth_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ def link_identity_from_session_data

def email_address_id
return nil unless IdentityConfig.store.feature_select_email_to_share_enabled
identity = current_user.identities.find_by(service_provider: sp_session[:issuer])
return nil if !identity&.verified_single_email_attribute?
if user_session[:selected_email_id_for_linked_identity].present?
return user_session[:selected_email_id_for_linked_identity]
end
identity = current_user.identities.find_by(service_provider: sp_session['issuer'])
email_id = identity&.email_address_id
return email_id if email_id.is_a? Integer
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/idv/welcome_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def create_document_capture_session
def cancel_previous_in_person_enrollments
return unless IdentityConfig.store.in_person_proofing_enabled
UspsInPersonProofing::EnrollmentHelper
.cancel_stale_establishing_enrollments_for_user(current_user)
.cancel_establishing_and_pending_enrollments(current_user)
end
end
end
4 changes: 3 additions & 1 deletion app/controllers/openid_connect/authorization_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ def link_identity_to_service_provider

def email_address_id
return nil unless IdentityConfig.store.feature_select_email_to_share_enabled
identity = current_user.identities.find_by(service_provider: sp_session[:issuer])
return nil if !identity&.verified_single_email_attribute?
if user_session[:selected_email_id_for_linked_identity].present?
return user_session[:selected_email_id_for_linked_identity]
end
identity = current_user.identities.find_by(service_provider: sp_session[:issuer])

identity&.email_address_id
end

Expand Down
29 changes: 24 additions & 5 deletions app/forms/reset_password_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def valid_token

def handle_valid_password
update_user
mark_profile_inactive
mark_profile_as_password_reset
end

def update_user
Expand All @@ -60,13 +60,24 @@ def update_user
user.update!(attributes)
end

def mark_profile_inactive
return if active_profile.blank?
def mark_profile_as_password_reset
profile = password_reset_profile
return if profile.blank?

active_profile.deactivate(:password_reset)
profile.deactivate(:password_reset)
Funnel::DocAuth::ResetSteps.call(user.id)
end

def password_reset_profile
FeatureManagement.pending_in_person_password_reset_enabled? ?
find_pending_in_person_or_active_profile :
active_profile
end

def find_pending_in_person_or_active_profile
user.pending_in_person_enrollment&.profile || active_profile
end

# It is possible for an account that is resetting their password to be "invalid".
# If an unconfirmed account (which must have one unconfirmed email address) resets their
# password and a different account then adds and confirms that same email address,
Expand All @@ -86,8 +97,16 @@ def extra_analytics_attributes
{
user_id: user.uuid,
profile_deactivated: active_profile.present?,
pending_profile_invalidated: pending_profile.present?,
pending_profile_invalidated: pending_profile_invalidated?,
pending_profile_pending_reasons: (pending_profile&.pending_reasons || [])&.join(','),
}
end

def pending_profile_invalidated?
if FeatureManagement.pending_in_person_password_reset_enabled?
pending_profile.present? && !pending_profile.in_person_verification_pending?
else
pending_profile.present?
end
end
end
2 changes: 1 addition & 1 deletion app/forms/verify_password_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def valid_password?

def reencrypt_pii
personal_key = profile.encrypt_pii(decrypted_pii, password)
profile.activate_after_password_reset
profile.clear_password_reset_deactivation_reason
personal_key
end

Expand Down
5 changes: 2 additions & 3 deletions app/forms/webauthn_visit_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ def check_params(params)
return unless error

if @platform_authenticator
errors.add error, translate_platform_authenticator_error(error),
type: :"#{translate_platform_authenticator_error(error).split('.').last}"
errors.add error, translate_platform_authenticator_error(error), type: :invalid
else
errors.add error, translate_error(error), type: :"#{translate_error(error).split('.').last}"
errors.add error, translate_error(error), type: :invalid
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/javascript/packages/phone-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"dependencies": {
"intl-tel-input": "^24.5.0",
"libphonenumber-js": "^1.11.18"
"libphonenumber-js": "^1.11.19"
},
"sideEffects": [
"./index.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

class CreateNewDeviceAlert < ApplicationJob
class CreateNewDeviceAlertJob < ApplicationJob
queue_as :long_running

def perform(now)
Expand Down
22 changes: 17 additions & 5 deletions app/jobs/get_usps_proofing_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def perform(_now)
enrollments_cancelled: 0,
enrollments_in_progress: 0,
enrollments_passed: 0,
enrollments_skipped: 0,
}

started_at = Time.zone.now
Expand Down Expand Up @@ -106,7 +107,7 @@ def check_enrollment(enrollment)

profile_deactivation_reason = enrollment.profile_deactivation_reason

if profile_deactivation_reason.present?
if profile_deactivation_reason.present? && profile_deactivation_reason != 'password_reset'
log_enrollment_updated_analytics(
enrollment: enrollment,
enrollment_passed: false,
Expand All @@ -118,9 +119,7 @@ def check_enrollment(enrollment)
return
end

response = proofer.request_proofing_results(
enrollment,
)
response = proofer.request_proofing_results(enrollment)
rescue Faraday::BadRequestError => err
# 400 status code. This is used for some status updates and some common client errors
handle_bad_request_error(err, enrollment)
Expand All @@ -131,7 +130,11 @@ def check_enrollment(enrollment)
rescue StandardError => err
handle_standard_error(err, enrollment)
else
process_enrollment_response(enrollment, response)
if profile_deactivation_reason == 'password_reset'
skip_enrollment(enrollment, profile_deactivation_reason)
else
process_enrollment_response(enrollment, response)
end
ensure
# Record the attempt to update the enrollment
enrollment.update(status_check_attempted_at: status_check_attempted_at)
Expand All @@ -143,6 +146,15 @@ def cancel_enrollment(enrollment)
enrollment.profile.deactivate_due_to_in_person_verification_cancelled
end

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

def passed_with_unsupported_secondary_id_type?(enrollment, response)
return false if enrollment.enhanced_ipp?

Expand Down
52 changes: 52 additions & 0 deletions app/jobs/grant_account_reset_requests_and_send_emails_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

class GrantAccountResetRequestsAndSendEmailsJob < ApplicationJob
queue_as :long_running

def perform(now)
notifications_sent = 0
AccountResetRequest.where(
sql_query_for_users_eligible_to_delete_their_accounts,
tvalue: now - IdentityConfig.store.account_reset_wait_period_days.days,
).order('requested_at ASC').each do |arr|
notifications_sent += 1 if grant_request_and_send_email(arr)
end

analytics.account_reset_notifications(count: notifications_sent)

notifications_sent
end

private

def analytics
@analytics ||= Analytics.new(
user: AnonymousUser.new,
request: nil,
sp: nil,
session: {},
)
end

def sql_query_for_users_eligible_to_delete_their_accounts
<<~SQL
cancelled_at IS NULL AND
granted_at IS NULL AND
requested_at < :tvalue AND
request_token IS NOT NULL AND
granted_token IS NULL
SQL
end

def grant_request_and_send_email(arr)
user = arr.user
return false unless AccountReset::GrantRequest.new(user).call

arr = arr.reload
user.confirmed_email_addresses.each do |email_address|
UserMailer.with(user: user, email_address: email_address)
.account_reset_granted(arr).deliver_now_or_later
end
true
end
end
2 changes: 2 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Event < ApplicationRecord
password_invalidated: 22,
sign_in_unsuccessful_2fa: 23,
sign_in_notification_timeframe_expired: 24,
webauthn_platform_added: 25,
webauthn_platform_removed: 26,
}

validates :event_type, presence: true
Expand Down
7 changes: 7 additions & 0 deletions app/models/in_person_enrollment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ def profile_deactivation_reason
profile&.deactivation_reason
end

# Updates the in-person enrollment to status cancelled and deactivates the
# associated profile with reason "in_person_verification_cancelled".
def cancel
cancelled!
profile&.deactivate_due_to_in_person_verification_cancelled
end

private

def days_to_expire
Expand Down
11 changes: 6 additions & 5 deletions app/models/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,14 @@ def activate_after_passing_in_person
end
end

def activate_after_password_reset
# Removes the deactivation reason from the profile if it had a password_reset
# deactivation reason. If the profile was activated previously it will be
# reactivated.
def clear_password_reset_deactivation_reason
if password_reset?
transaction do
update!(
deactivation_reason: nil,
)
activate(reason_deactivated: :password_reset)
update!(deactivation_reason: nil)
activate(reason_deactivated: :password_reset) if activated_at.present?
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions app/models/service_provider_identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class ServiceProviderIdentity < ApplicationRecord

belongs_to :user
validates :service_provider, presence: true
before_save :clear_email_address_id_if_not_supported

# rubocop:disable Rails/InverseOf
belongs_to :deleted_user, foreign_key: 'user_id', primary_key: 'user_id'
Expand Down Expand Up @@ -71,6 +72,13 @@ def verified_single_email_attribute?
!verified_attributes.include?('all_emails')
end

def clear_email_address_id_if_not_supported
if !verified_single_email_attribute? &&
IdentityConfig.store.feature_select_email_to_share_enabled
self.email_address_id = nil
end
end

def email_address_for_sharing
if IdentityConfig.store.feature_select_email_to_share_enabled && email_address
return email_address
Expand Down
22 changes: 19 additions & 3 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,11 @@ def identity_verified_with_facial_match?
active_profile.present? && active_profile.facial_match?
end

# This user's most recently activated profile that has also been deactivated
# due to a password reset, or nil if there is no such profile
# The users most recently activated or pending in person enrollment profile
# that has also been deactivated due to a password reset, or nil if there is
# no such profile
def password_reset_profile
profile = profiles.where.not(activated_at: nil).order(activated_at: :desc).first
profile = find_password_reset_profile
profile if profile&.password_reset?
end

Expand Down Expand Up @@ -534,6 +535,21 @@ def last_sign_in_email_address

private

def find_password_reset_profile
FeatureManagement.pending_in_person_password_reset_enabled? ?
find_pending_in_person_or_active_profile :
find_active_profile
end

def find_active_profile
profiles.where.not(activated_at: nil).order(activated_at: :desc).first
end

def find_pending_in_person_or_active_profile
pending_in_person_enrollment&.profile ||
profiles.where.not(activated_at: nil).order(activated_at: :desc).first
end

def lockout_period
IdentityConfig.store.lockout_period_in_minutes.minutes
end
Expand Down
5 changes: 3 additions & 2 deletions app/policies/pending_profile_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ def user_has_pending_profile?
attr_reader :user, :resolved_authn_context_result

def pending_facial_match_profile?
user.pending_profile&.idv_level == 'unsupervised_with_selfie'
Profile::FACIAL_MATCH_IDV_LEVELS.include?(user.pending_profile&.idv_level)
end

def facial_match_requested?
resolved_authn_context_result.facial_match?
end

def pending_legacy_profile?
user.pending_profile.present? && user.pending_profile&.idv_level != 'unsupervised_with_selfie'
user.pending_profile&.present? &&
user.pending_profile.idv_level != 'unsupervised_with_selfie'
end

def fraud_review_pending?
Expand Down
Loading

0 comments on commit ec142ef

Please sign in to comment.