Skip to content

Commit

Permalink
Merge pull request #11715 from 18F/stages/rc-2025-01-07
Browse files Browse the repository at this point in the history
Deploy RC 442 to Production
  • Loading branch information
solipet authored Jan 7, 2025
2 parents faae3a7 + 304493b commit a082cb3
Show file tree
Hide file tree
Showing 71 changed files with 879 additions and 345 deletions.
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ specs:
- cp -a keys.example keys
- cp -a certs.example certs
- cp pwned_passwords/pwned_passwords.txt.sample pwned_passwords/pwned_passwords.txt
- "echo -e \"test:\n redis_url: 'redis://db-redis:6379/0'\n redis_throttle_url: 'redis://db-redis:6379/1'\" > config/application.yml"
- "echo -e \"test:\n redis_url: 'redis://db-redis:6379/0'\n redis_throttle_url: 'redis://db-redis:6379/1'\n redis_attempts_api_url: 'redis://db-redis:6379/2'\" > config/application.yml"
- bundle exec rake db:create db:migrate --trace
- bundle exec rake db:seed
- bundle exec rake knapsack:rspec["--format documentation --format RspecJunitFormatter --out rspec.xml --format json --out rspec_json/${CI_NODE_INDEX}.json"]
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ GEM
net-smtp (0.5.0)
net-protocol
net-ssh (6.1.0)
newrelic_rpm (9.7.0)
newrelic_rpm (9.16.1)
nio4r (2.7.4)
nokogiri (1.16.8)
mini_portile2 (~> 2.8.2)
Expand Down
6 changes: 0 additions & 6 deletions app/controllers/concerns/threat_metrix_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ module ThreatMetrixConcern
THREAT_METRIX_WILDCARD_DOMAIN = '*.online-metrix.net'

def override_csp_for_threat_metrix
return unless FeatureManagement.proofing_device_profiling_collecting_enabled?

threat_metrix_csp_overrides
end

def threat_metrix_csp_overrides
policy = current_content_security_policy

# ThreatMetrix requires additional Content Security Policy (CSP)
Expand Down
11 changes: 8 additions & 3 deletions app/controllers/concerns/two_factor_authenticatable_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,14 @@ def sign_in_notification_timeframe_expired_event
return @sign_in_notification_timeframe_expired_event if defined?(
@sign_in_notification_timeframe_expired_event
)
@sign_in_notification_timeframe_expired_event = current_user.events.where(
event_type: 'sign_in_notification_timeframe_expired',
).order(created_at: :desc).limit(1).take
@sign_in_notification_timeframe_expired_event = current_user.events
.where(
event_type: 'sign_in_notification_timeframe_expired',
created_at: (IdentityConfig.store.session_total_duration_timeout_in_minutes.minutes.ago..),
)
.order(created_at: :desc)
.limit(1)
.take
end

def handle_remember_device_preference(remember_device_preference)
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/idv/forgot_password_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def new
def update
analytics.idv_forgot_password_confirmed
request_id = sp_session[:request_id]
email = current_user.confirmed_email_addresses.first.email
email = current_user.last_sign_in_email_address.email
reset_password(email, request_id)
end

Expand Down
2 changes: 2 additions & 0 deletions app/controllers/idv/in_person/ready_to_verify_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class ReadyToVerifyController < ApplicationController

def show
@is_enhanced_ipp = resolved_authn_context_result.enhanced_ipp?
@show_closed_post_office_banner =
IdentityConfig.store.in_person_proofing_post_office_closed_alert_enabled
analytics.idv_in_person_ready_to_verify_visit(**opt_in_analytics_properties)
@presenter = ReadyToVerifyPresenter.new(
enrollment: enrollment,
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/idv/in_person/ssn_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class SsnController < ApplicationController
before_action :confirm_not_rate_limited_after_doc_auth
before_action :confirm_in_person_address_step_complete
before_action :confirm_repeat_ssn, only: :show
before_action :override_csp_for_threat_metrix
before_action :override_csp_for_threat_metrix,
if: -> { FeatureManagement.proofing_device_profiling_collecting_enabled? }

attr_reader :ssn_presenter

Expand Down
3 changes: 2 additions & 1 deletion app/controllers/idv/ssn_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class SsnController < ApplicationController

before_action :confirm_not_rate_limited_after_doc_auth
before_action :confirm_step_allowed
before_action :override_csp_for_threat_metrix
before_action :override_csp_for_threat_metrix,
if: -> { FeatureManagement.proofing_device_profiling_collecting_enabled? }

attr_reader :ssn_presenter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ def create
increment_mfa_selection_attempt_count(otp_auth_method)
end
result = otp_verification_form.submit
post_analytics(result)

if UserSessionContext.confirmation_context?(context)
log_confirmation_analytics(result)
end

if UserSessionContext.authentication_or_reauthentication_context?(context)
handle_verification_for_authentication_context(
Expand Down Expand Up @@ -149,9 +152,9 @@ def form_params
params.permit(:code)
end

def post_analytics(result)
def log_confirmation_analytics(result)
properties = result.to_h.merge(analytics_properties)
analytics.multi_factor_auth_setup(**properties) if context == 'confirmation'
analytics.multi_factor_auth_setup(**properties)
end

def analytics_properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class TwoFactorAuthenticationSetupController < ApplicationController
before_action :authenticate_user
before_action :confirm_user_authenticated_for_2fa_setup
before_action :check_if_possible_piv_user
before_action :override_csp_for_threat_metrix
before_action :override_csp_for_threat_metrix,
if: -> { FeatureManagement.account_creation_device_profiling_collecting_enabled? }

delegate :enabled_mfa_methods_count, to: :mfa_context

Expand Down
2 changes: 2 additions & 0 deletions app/controllers/users/webauthn_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def new
increment_mfa_selection_attempt_count(webauthn_auth_method)
analytics.webauthn_setup_submitted(
platform_authenticator: form.platform_authenticator?,
in_account_creation_flow: user_session[:in_account_creation_flow] || false,
errors: result.errors,
success: false,
)
Expand Down Expand Up @@ -129,6 +130,7 @@ def process_valid_webauthn(form)
create_user_event(:webauthn_key_added)
analytics.webauthn_setup_submitted(
platform_authenticator: form.platform_authenticator?,
in_account_creation_flow: user_session[:in_account_creation_flow] || false,
success: true,
)
handle_remember_device_preference(params[:remember_device])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
submissionError instanceof UploadFormEntriesError
? withProps({
remainingSubmitAttempts: submissionError.remainingSubmitAttempts,
submitAttempts: submissionError.submitAttempts,
isResultCodeInvalid: submissionError.isResultCodeInvalid,
isFailedResult: submissionError.isFailedResult,
isFailedDocType: submissionError.isFailedDocType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { Button } from '@18f/identity-components';
import { useInstanceId } from '@18f/identity-react-hooks';
import { t } from '@18f/identity-i18n';
import AnalyticsContext from '../context/analytics';
import UploadContext from '../context/upload';

function InPersonCallToAction() {
const instanceId = useInstanceId();
const { trackEvent } = useContext(AnalyticsContext);
const { submitAttempts } = useContext(UploadContext);

return (
<section
Expand All @@ -25,7 +27,9 @@ function InPersonCallToAction() {
isWide
className="margin-top-3 margin-bottom-1"
onClick={() => {
trackEvent('IdV: verify in person troubleshooting option clicked');
trackEvent('IdV: verify in person troubleshooting option clicked', {
submit_attempts: submitAttempts,
});
}}
>
{t('in_person_proofing.body.cta.button')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface ReviewIssuesStepValue {

interface ReviewIssuesStepProps extends FormStepComponentProps<ReviewIssuesStepValue> {
remainingSubmitAttempts?: number;
submitAttempts?: number;
isResultCodeInvalid?: boolean;
isFailedResult?: boolean;
isFailedSelfie?: boolean;
Expand All @@ -57,6 +58,7 @@ function ReviewIssuesStep({
registerField = () => undefined,
toPreviousStep = () => undefined,
remainingSubmitAttempts = Infinity,
submitAttempts,
isResultCodeInvalid = false,
isFailedResult = false,
isFailedDocType = false,
Expand Down Expand Up @@ -106,6 +108,7 @@ function ReviewIssuesStep({
function onWarningPageDismissed() {
trackEvent('IdV: Capture troubleshooting dismissed', {
liveness_checking_required: isSelfieCaptureEnabled,
submit_attempts: submitAttempts,
});

setHasDismissed(true);
Expand Down
25 changes: 22 additions & 3 deletions app/javascript/packages/document-capture/context/upload.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext } from 'react';
import { createContext, useState } from 'react';
import { useObjectMemo } from '@18f/identity-react-hooks';
import type { ReactNode } from 'react';
import defaultUpload from '../services/upload';
import defaultUpload, { UploadFormEntriesError } from '../services/upload';
import type { PII } from '../services/upload';

const UploadContext = createContext({
Expand All @@ -11,6 +11,7 @@ const UploadContext = createContext({
isMockClient: false,
flowPath: 'standard' as FlowPath,
formData: {} as Record<string, any>,
submitAttempts: 0,
});

UploadContext.displayName = 'UploadContext';
Expand Down Expand Up @@ -80,6 +81,11 @@ export interface UploadErrorResponse {
*/
remaining_submit_attempts?: number;

/**
* Number of submitted doc capture attempts for user
*/
submit_attempts?: number;

/**
* Boolean to decide if capture hints should be shown with error.
*/
Expand Down Expand Up @@ -189,7 +195,19 @@ function UploadContextProvider({
flowPath,
children,
}: UploadContextProviderProps) {
const uploadWithFormData = (payload) => upload({ ...payload, ...formData }, { endpoint });
const [submitAttempts, setSubmitAttempts] = useState(0);

const uploadWithFormData = async (payload) => {
try {
const result = await upload({ ...payload, ...formData }, { endpoint });
return result;
} catch (error) {
if (error instanceof UploadFormEntriesError && error.submitAttempts !== undefined) {
setSubmitAttempts(error.submitAttempts);
}
throw error;
}
};

const getStatus = () =>
statusEndpoint
Expand All @@ -203,6 +221,7 @@ function UploadContextProvider({
isMockClient,
flowPath,
formData,
submitAttempts,
});

return <UploadContext.Provider value={value}>{children}</UploadContext.Provider>;
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/packages/document-capture/services/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export class UploadFormEntriesError extends FormError {

remainingSubmitAttempts = Infinity;

submitAttempts = 0;

isResultCodeInvalid = false;

isFailedResult = false;
Expand Down Expand Up @@ -120,6 +122,10 @@ const upload: UploadImplementation = async function (payload, { method = 'POST',
error.remainingSubmitAttempts = result.remaining_submit_attempts;
}

if (result.submit_attempts) {
error.submitAttempts = result.submit_attempts;
}

if (result.ocr_pii) {
error.pii = result.ocr_pii;
}
Expand Down
3 changes: 2 additions & 1 deletion app/jobs/resolution_proofing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def perform(
service_provider_issuer: nil,
threatmetrix_session_id: nil,
request_ip: nil,
proofing_components: nil, # rubocop:disable Lint/UnusedMethodArgument
# DEPRECATED ARGUMENTS
should_proof_state_id: false # rubocop:disable Lint/UnusedMethodArgument
)
Expand Down Expand Up @@ -130,7 +131,7 @@ def make_vendor_proofing_requests(
end

def user_email_for_proofing(user)
user.confirmed_email_addresses.first.email
user.last_sign_in_email_address.email
end

def log_threatmetrix_info(threatmetrix_result, user)
Expand Down
18 changes: 16 additions & 2 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,6 @@ def idv_please_call(**)
end
end

alias_method :in_person_please_call, :idv_please_call

def in_person_completion_survey
with_user_locale(user) do
@header = t('user_mailer.in_person_completion_survey.header')
Expand Down Expand Up @@ -314,6 +312,9 @@ def in_person_ready_to_verify(enrollment:, is_enhanced_ipp:)
is_enhanced_ipp: is_enhanced_ipp,
)
@is_enhanced_ipp = is_enhanced_ipp
@show_closed_post_office_banner =
IdentityConfig.store.in_person_proofing_post_office_closed_alert_enabled

mail(
to: email_address.email,
subject: t('user_mailer.in_person_ready_to_verify.subject', app_name: APP_NAME),
Expand All @@ -327,6 +328,9 @@ def in_person_ready_to_verify_reminder(enrollment:)
).image_data

@is_enhanced_ipp = enrollment.enhanced_ipp?
@show_closed_post_office_banner =
IdentityConfig.store.in_person_proofing_post_office_closed_alert_enabled

with_user_locale(user) do
@presenter = Idv::InPerson::ReadyToVerifyPresenter.new(
enrollment: enrollment,
Expand Down Expand Up @@ -435,6 +439,16 @@ def account_reinstated
end
end

def in_person_post_office_closed
with_user_locale(user) do
@hide_title = true
mail(
to: email_address.email,
subject: t('in_person_proofing.post_office_closed.email.subject'),
)
end
end

private

attr_reader :user, :email_address
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class User < ApplicationRecord
attr_accessor :asserted_attributes, :email

def confirmed_email_addresses
email_addresses.confirmed.order('last_sign_in_at DESC NULLS LAST')
email_addresses.confirmed
end

def fully_registered?
Expand Down
5 changes: 5 additions & 0 deletions app/presenters/image_upload_response_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def remaining_submit_attempts
@form_response.to_h[:remaining_submit_attempts]
end

def submit_attempts
@form_response.to_h[:submit_attempts]
end

def status
if success?
:ok
Expand All @@ -41,6 +45,7 @@ def as_json(*)
json = { success: false,
errors: errors,
remaining_submit_attempts: remaining_submit_attempts,
submit_attempts: submit_attempts,
doc_type_supported: doc_type_supported? }
if remaining_submit_attempts&.zero?
if @form_response.extra[:flow_path] == 'standard'
Expand Down
Loading

0 comments on commit a082cb3

Please sign in to comment.