Skip to content

Commit

Permalink
Merge pull request #11584 from 18F/stages/rc-2024-12-03
Browse files Browse the repository at this point in the history
Deploy RC 434 to Production
  • Loading branch information
matthinz authored Dec 3, 2024
2 parents d654ec4 + 0a42941 commit b5dba56
Show file tree
Hide file tree
Showing 90 changed files with 1,324 additions and 262 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SelectedEmailController < ApplicationController
def edit
@identity = identity
@select_email_form = build_select_email_form
@can_add_email = EmailPolicy.new(current_user).can_add_email?
analytics.sp_select_email_visited
end

Expand Down
11 changes: 0 additions & 11 deletions app/controllers/concerns/idv/document_capture_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ module DocumentCaptureConcern

def handle_stored_result(user: current_user, store_in_session: true)
if stored_result&.success? && selfie_requirement_met?
save_proofing_components(user)
extract_pii_from_doc(user, store_in_session: store_in_session)
flash[:success] = t('doc_auth.headings.capture_complete')
successful_response
Expand All @@ -18,16 +17,6 @@ def handle_stored_result(user: current_user, store_in_session: true)
end
end

def save_proofing_components(user)
return unless user

component_attributes = {
document_check: doc_auth_vendor,
document_type: 'state_id',
}
ProofingComponent.create_or_find_by(user: user).update(component_attributes)
end

def successful_response
FormResponse.new(success: true)
end
Expand Down
17 changes: 1 addition & 16 deletions app/controllers/concerns/idv/verify_info_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -267,26 +267,11 @@ def summarize_result_and_rate_limit(summary_result)
proofing_results_exception = summary_result.extra.dig(:proofing_results, :exception)
resolution_rate_limiter.increment! if proofing_results_exception.blank?

if summary_result.success?
add_proofing_components(summary_result)
else
if !summary_result.success?
idv_failure(summary_result)
end
end

def add_proofing_components(summary_result)
ProofingComponent.create_or_find_by(user: current_user).update(
resolution_check: Idp::Constants::Vendors::LEXIS_NEXIS,
source_check: summary_result.extra.dig(
:proofing_results,
:context,
:stages,
:state_id,
:vendor_name,
),
)
end

def load_async_state
dcs_uuid = idv_session.verify_info_step_document_capture_session_uuid
dcs = DocumentCaptureSession.find_by(uuid: dcs_uuid)
Expand Down
2 changes: 0 additions & 2 deletions app/controllers/idv/by_mail/request_letter_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ def update_tracking

log_letter_requested_analytics(resend: false)
create_user_event(:gpo_mail_sent, current_user)

ProofingComponent.find_or_create_by(user: current_user).update(address_check: 'gpo_letter')
end

def confirm_mail_not_rate_limited
Expand Down
12 changes: 2 additions & 10 deletions app/controllers/idv/in_person/usps_locations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,9 @@ def update
sponsor_id: enrollment_sponsor_id,
)

add_proofing_component

render json: { success: true }, status: :ok
end

private

def idv_session
if user_session && current_user
@idv_session ||= Idv::Session.new(
Expand All @@ -80,6 +76,8 @@ def idv_session
end
end

private

def document_capture_session
if idv_session&.document_capture_session_uuid # standard flow
DocumentCaptureSession.find_by(uuid: idv_session.document_capture_session_uuid)
Expand All @@ -92,12 +90,6 @@ def proofer
@proofer ||= EnrollmentHelper.usps_proofer
end

def add_proofing_component
ProofingComponent.
create_or_find_by(user: current_or_hybrid_user).
update(document_check: Idp::Constants::Vendors::USPS)
end

def localized_locations(locations)
return nil if locations.nil?
locations.map do |location|
Expand Down
1 change: 0 additions & 1 deletion app/controllers/idv/link_sent_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def analytics_arguments
end

def handle_document_verification_success
save_proofing_components(current_user)
extract_pii_from_doc(current_user, store_in_session: true)
idv_session.flow_path = 'hybrid'
end
Expand Down
5 changes: 0 additions & 5 deletions app/controllers/idv/personal_key_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def show
if pii_is_missing?
redirect_to_retrieve_pii
else
add_proofing_component
finish_idv_session
end
end
Expand Down Expand Up @@ -78,10 +77,6 @@ def next_step
end
end

def add_proofing_component
ProofingComponent.find_or_create_by(user: current_user).update(verified_at: Time.zone.now)
end

def finish_idv_session
@code = personal_key
@personal_key_generated_at = current_user.personal_key_generated_at
Expand Down
10 changes: 2 additions & 8 deletions app/controllers/redirect/redirect_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,8 @@ def partner_params
}.compact
end

def redirect_to_and_log(url, event: nil, tracker_method: analytics.method(:external_redirect))
if event
# Once all events have been moved to tracker methods, we can remove the event: param
analytics.track_event(event, redirect_url: url, **location_params)
else
tracker_method.call(redirect_url: url, **location_params)
end

def redirect_to_and_log(url, tracker_method: analytics.method(:external_redirect))
tracker_method.call(redirect_url: url, **location_params)
redirect_url = UriService.add_params(url, partner_params)
redirect_to(redirect_url, allow_other_host: true)
end
Expand Down
1 change: 1 addition & 0 deletions app/controllers/sign_up/select_email_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def show
@user_emails = user_emails
@last_sign_in_email_address = last_email
@select_email_form = build_select_email_form
@can_add_email = EmailPolicy.new(current_user).can_add_email?
analytics.sp_select_email_visited(needs_completion_screen_reason:)
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/socure_webhook_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def fetch_results
if IdentityConfig.store.ruby_workers_idv_enabled
SocureDocvResultsJob.perform_later(document_capture_session_uuid: dcs.uuid)
else
SocureDocvResultsJob.perform_now(document_capture_session_uuid: dcs.uuid)
SocureDocvResultsJob.perform_now(document_capture_session_uuid: dcs.uuid, async: false)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def mark_profile_inactive

user.active_profile&.deactivate(:password_reset)
Funnel::DocAuth::ResetSteps.call(@user.id)
user.proofing_component&.destroy
end

def extra_analytics_attributes
Expand Down
1 change: 0 additions & 1 deletion app/forms/reset_password_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def mark_profile_inactive

active_profile.deactivate(:password_reset)
Funnel::DocAuth::ResetSteps.call(user.id)
user.proofing_component&.destroy
end

# It is possible for an account that is resetting their password to be "invalid".
Expand Down
30 changes: 30 additions & 0 deletions app/javascript/packages/webauthn/is-expected-error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,34 @@ describe('isExpectedWebauthnError', () => {

expect(result).to.be.true();
});

it('returns true for a NotReadableError Android credential manager incompatibility', () => {
const error = new DOMException(
'An unknown error occurred while talking to the credential manager.',
'NotReadableError',
);
const result = isExpectedWebauthnError(error);

expect(result).to.be.true();
});

it('returns false for NotSupportedError when not during verification', () => {
const error = new DOMException(
'The user agent does not support public key credentials.',
'NotSupportedError',
);
const result = isExpectedWebauthnError(error);

expect(result).to.be.false();
});

it('returns true for NotSupportedError during verification', () => {
const error = new DOMException(
'The user agent does not support public key credentials.',
'NotSupportedError',
);
const result = isExpectedWebauthnError(error, { isVerifying: true });

expect(result).to.be.true();
});
});
50 changes: 30 additions & 20 deletions app/javascript/packages/webauthn/is-expected-error.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import isUserVerificationScreenLockError from './is-user-verification-screen-lock-error';

/**
* Set of expected DOM exceptions, which occur based on some user behavior that is not noteworthy:
*
* - Declining permissions
* - Timeout due to inactivity
* - Invalid state such as duplicate key enrollment
*
* @see https://webidl.spec.whatwg.org/#idl-DOMException
* Functions to test whether an error is expected and should not be logged for further analysis.
*/
const EXPECTED_DOM_EXCEPTIONS: Set<string> = new Set([
'NotAllowedError',
'TimeoutError',
'InvalidStateError',
]);
const EXPECTED_ERRORS: Array<(error: Error, options: IsExpectedErrorOptions) => boolean> = [
// A user who is unable to complete due to following DOMException reasons is not noteworthy:
//
// - Declining permissions
// - Timeout due to inactivity
// - Invalid state such as duplicate key enrollment
(error) =>
error.name === 'NotAllowedError' ||
error.name === 'TimeoutError' ||
error.name === 'InvalidStateError',
// Some indication of incompatibilities on specific Android devices, either phone itself or
// through credential manager.
//
// See: https://community.bitwarden.com/t/android-mobile-yubikey-5-nfc-webauth/51732
// See: https://www.reddit.com/r/GooglePixel/comments/17enqf3/pixel_7_pro_unable_to_setup_passkeys/
(error) =>
error.name === 'NotReadableError' &&
error.message === 'An unknown error occurred while talking to the credential manager.',
// A user can choose to authenticate with Face or Touch Unlock from another device from what
// they set up from, which may not necessarily support platform authenticators.
(error, { isVerifying }) => isVerifying && isUserVerificationScreenLockError(error),
(error, { isVerifying }) =>
isVerifying &&
error.name === 'NotSupportedError' &&
error.message === 'The user agent does not support public key credentials.',
];

interface IsExpectedErrorOptions {
/**
Expand All @@ -22,14 +37,9 @@ interface IsExpectedErrorOptions {
isVerifying: boolean;
}

function isExpectedWebauthnError(
const isExpectedWebauthnError = (
error: Error,
{ isVerifying }: Partial<IsExpectedErrorOptions> = {},
): boolean {
return (
(error instanceof DOMException && EXPECTED_DOM_EXCEPTIONS.has(error.name)) ||
(!!isVerifying && isUserVerificationScreenLockError(error))
);
}
{ isVerifying = false }: Partial<IsExpectedErrorOptions> = {},
): boolean => EXPECTED_ERRORS.some((isExpected) => isExpected(error, { isVerifying }));

export default isExpectedWebauthnError;
4 changes: 4 additions & 0 deletions app/jobs/data_warehouse/base_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@ def upload_file_to_s3_bucket(path:, body:, content_type:, bucket: bucket_name)
logger.debug("#{class_name}: upload completed to #{url}")
url
end

def data_warehouse_disabled?
!IdentityConfig.store.data_warehouse_enabled
end
end
end
2 changes: 2 additions & 0 deletions app/jobs/data_warehouse/table_summary_stats_export_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class TableSummaryStatsExportJob < BaseJob
REPORT_NAME = 'table_summary_stats'

def perform(timestamp)
return if data_warehouse_disabled?

data = fetch_table_max_ids_and_counts(timestamp)
upload_to_s3(data, timestamp)
end
Expand Down
21 changes: 12 additions & 9 deletions app/jobs/resolution_proofing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def perform(
timing: timer.results,
)

if IdentityConfig.store.idv_socure_shadow_mode_enabled
if use_shadow_mode?(user:)
SocureShadowModeProofingJob.perform_later(
document_capture_session_result_id: document_capture_session&.result_id,
encrypted_arguments:,
Expand All @@ -85,6 +85,17 @@ def perform(
end
end

def use_shadow_mode?(user:)
IdentityConfig.store.idv_socure_shadow_mode_enabled &&
AbTests::SOCURE_IDV_SHADOW_MODE.bucket(
request: nil,
service_provider: nil,
session: nil,
user:,
user_session: nil,
) == :shadow_mode_enabled
end

private

# @return [CallbackLogData]
Expand All @@ -108,7 +119,6 @@ def make_vendor_proofing_requests(
)

log_threatmetrix_info(result.device_profiling_result, user)
add_threatmetrix_proofing_component(user.id, result.device_profiling_result) if user.present?

CallbackLogData.new(
device_profiling_success: result.device_profiling_result.success?,
Expand Down Expand Up @@ -139,11 +149,4 @@ def logger_info_hash(hash)
def progressive_proofer
@progressive_proofer ||= Proofing::Resolution::ProgressiveProofer.new
end

def add_threatmetrix_proofing_component(user_id, threatmetrix_result)
ProofingComponent.
create_or_find_by(user_id: user_id).
update(threatmetrix: FeatureManagement.proofing_device_profiling_collecting_enabled?,
threatmetrix_review_status: threatmetrix_result.review_status)
end
end
Loading

0 comments on commit b5dba56

Please sign in to comment.