Skip to content
Closed
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
4 changes: 3 additions & 1 deletion app/controllers/idv/otp_delivery_method_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ def render_new_with_error_message
def send_phone_confirmation_otp_and_handle_result
save_delivery_preference
result = send_phone_confirmation_otp
analytics.idv_phone_confirmation_otp_sent(**result.to_h)
analytics.idv_phone_confirmation_otp_sent(
**result.to_h.merge(adapter: Telephony.config.adapter),
)

irs_attempts_api_tracker.idv_phone_otp_sent(
phone_number: @idv_phone,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ def track_events(otp_delivery_preference:)
context: context,
otp_delivery_preference: otp_delivery_preference,
resend: params.dig(:otp_delivery_selection_form, :resend),
adapter: Telephony.config.adapter,
telephony_response: @telephony_result.to_h,
success: @telephony_result.success?,
)
Expand Down
1 change: 1 addition & 0 deletions app/forms/add_user_email_form.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class AddUserEmailForm
include ActiveModel::Model
include FormAddEmailValidator
include ActionView::Helpers::TranslationHelper

attr_reader :email

Expand Down
16 changes: 6 additions & 10 deletions app/forms/idv/api_image_upload_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,6 @@ def as_readable(image_key)
end
end

def track_event(event, attributes = {})
if analytics.present?
analytics.track_event(
event,
attributes,
)
end
end

def update_analytics(client_response)
add_costs(client_response)
update_funnel(client_response)
Expand All @@ -219,13 +210,16 @@ def update_analytics(client_response)
).merge(native_camera_ab_test_data),
)
pii_from_doc = client_response.pii_from_doc || {}
store_encrypted_images_if_required
stored_image_result = store_encrypted_images_if_required
irs_attempts_api_tracker.idv_document_upload_submitted(
success: client_response.success?,
document_state: pii_from_doc[:state],
document_number: pii_from_doc[:state_id_number],
document_issued: pii_from_doc[:state_id_issued],
document_expiration: pii_from_doc[:state_id_expiration],
document_front_image_filename: stored_image_result&.front_filename,
document_back_image_filename: stored_image_result&.back_filename,
document_image_encryption_key: stored_image_result&.encryption_key,
first_name: pii_from_doc[:first_name],
last_name: pii_from_doc[:last_name],
date_of_birth: pii_from_doc[:dob],
Expand All @@ -239,7 +233,9 @@ def store_encrypted_images_if_required

encrypted_document_storage_writer.encrypt_and_write_document(
front_image: front_image_bytes,
front_image_content_type: front.content_type,
back_image: back_image_bytes,
back_image_content_type: back.content_type,
)
end

Expand Down
20 changes: 1 addition & 19 deletions app/forms/idv/inherited_proofing/base_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,22 @@ def model_name
def namespaced_model_name
self.to_s.gsub('::', '')
end

def fields
@fields ||= required_fields + optional_fields
end

def required_fields
raise NotImplementedError,
'Override this method and return an Array of required field names as Symbols'
end

def optional_fields
raise NotImplementedError,
'Override this method and return an Array of optional field names as Symbols'
end
end

private_class_method :namespaced_model_name, :required_fields, :optional_fields
private_class_method :namespaced_model_name

attr_reader :payload_hash

def initialize(payload_hash:)
raise ArgumentError, 'payload_hash is blank?' if payload_hash.blank?
raise ArgumentError, 'payload_hash is not a Hash' unless payload_hash.is_a? Hash

self.class.attr_accessor(*self.class.fields)

@payload_hash = payload_hash.dup

populate_field_data
end

def submit
validate

FormResponse.new(
success: valid?,
errors: errors,
Expand Down
54 changes: 43 additions & 11 deletions app/forms/idv/inherited_proofing/va/form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,34 @@ module Idv
module InheritedProofing
module Va
class Form < Idv::InheritedProofing::BaseForm
class << self
def required_fields
@required_fields ||= %i[first_name last_name birth_date ssn address_street address_zip]
end

def optional_fields
@optional_fields ||= %i[phone address_street2 address_city address_state
address_country]
end
end
REQUIRED_FIELDS = %i[first_name
last_name
birth_date
ssn
address_street
address_zip].freeze
OPTIONAL_FIELDS = %i[phone
address_street2
address_city
address_state
address_country
service_error].freeze
FIELDS = (REQUIRED_FIELDS + OPTIONAL_FIELDS).freeze

attr_accessor(*FIELDS)
validate :add_service_error, if: :service_error?
validates(*REQUIRED_FIELDS, presence: true, unless: :service_error?)

validates(*required_fields, presence: true)
def submit
extra = {}
extra = { service_error: service_error } if service_error?

FormResponse.new(
success: validate,
errors: errors,
extra: extra,
)
end

def user_pii
raise 'User PII is invalid' unless valid?
Expand All @@ -30,6 +46,22 @@ def user_pii
user_pii[:zipcode] = address_zip
user_pii
end

def service_error?
service_error.present?
end

private

def add_service_error
errors.add(
:service_provider,
# Use a "safe" error message for the model in case it's displayed
# to the user at any point.
I18n.t('inherited_proofing.errors.service_provider.communication'),
type: :service_error,
)
end
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/javascript/packages/document-capture/context/acuant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ const getActualAcuantCamera = (): AcuantCameraInterface => {
};

function AcuantContextProvider({
sdkSrc = '/acuant/11.7.0/AcuantJavascriptWebSdk.min.js',
cameraSrc = '/acuant/11.7.0/AcuantCamera.min.js',
sdkSrc = '/acuant/11.7.1/AcuantJavascriptWebSdk.min.js',
cameraSrc = '/acuant/11.7.1/AcuantCamera.min.js',
credentials = null,
endpoint = null,
glareThreshold = DEFAULT_ACCEPTABLE_GLARE_SCORE,
Expand Down
16 changes: 14 additions & 2 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,12 @@ def idv_inherited_proofing_get_started_visited(flow_path:, step:, **extra)
)
end

# Retry retrieving the user PII in the case where the first attempt fails
# in the agreement step, and the user initiates a "retry".
def idv_inherited_proofing_redo_retrieve_user_info_submitted(**extra)
track_event('IdV: inherited proofing retry retrieve user information submitted', **extra)
end

# @param [String] flow_path Document capture path ("hybrid" or "standard")
# The user visited the in person proofing location step
def idv_in_person_location_visited(flow_path:, **extra)
Expand Down Expand Up @@ -1146,7 +1152,7 @@ def idv_phone_confirmation_otp_rate_limit_sends(proofing_components: nil, **extr

# @param [Boolean] success
# @param [Hash] errors
# @param ["sms","voice"] otp_delivery_preference which chaennel the OTP was delivered by
# @param ["sms","voice"] otp_delivery_preference which channel the OTP was delivered by
# @param [String] country_code country code of phone number
# @param [String] area_code area code of phone number
# @param [Boolean] rate_limit_exceeded whether or not the rate limit was exceeded by this attempt
Expand Down Expand Up @@ -1180,13 +1186,14 @@ def idv_phone_confirmation_otp_resent(

# @param [Boolean] success
# @param [Hash] errors
# @param ["sms","voice"] otp_delivery_preference which chaennel the OTP was delivered by
# @param ["sms","voice"] otp_delivery_preference which channel the OTP was delivered by
# @param [String] country_code country code of phone number
# @param [String] area_code area code of phone number
# @param [Boolean] rate_limit_exceeded whether or not the rate limit was exceeded by this attempt
# @param [String] phone_fingerprint the hmac fingerprint of the phone number formatted as e164
# @param [Hash] telephony_response response from Telephony gem
# @param [Idv::ProofingComponentsLogging] proofing_components User's current proofing components
# @param [:test, :pinpoint] adapter which adapter the OTP was delivered with
# The user requested an OTP to confirm their phone during the IDV phone step
def idv_phone_confirmation_otp_sent(
success:,
Expand All @@ -1197,6 +1204,7 @@ def idv_phone_confirmation_otp_sent(
rate_limit_exceeded:,
phone_fingerprint:,
telephony_response:,
adapter:,
proofing_components: nil,
**extra
)
Expand All @@ -1210,6 +1218,7 @@ def idv_phone_confirmation_otp_sent(
rate_limit_exceeded: rate_limit_exceeded,
phone_fingerprint: phone_fingerprint,
telephony_response: telephony_response,
adapter: adapter,
proofing_components: proofing_components,
**extra,
)
Expand Down Expand Up @@ -2644,6 +2653,7 @@ def sms_opt_in_visit(
# @param ["sms","voice"] otp_delivery_preference the channel used to send the message
# @param [Boolean] resend
# @param [Hash] telephony_response
# @param [:test, :pinpoint] adapter which adapter the OTP was delivered with
# @param [Boolean] success
# A phone one-time password send was attempted
def telephony_otp_sent(
Expand All @@ -2654,6 +2664,7 @@ def telephony_otp_sent(
otp_delivery_preference:,
resend:,
telephony_response:,
adapter:,
success:,
**extra
)
Expand All @@ -2667,6 +2678,7 @@ def telephony_otp_sent(
otp_delivery_preference: otp_delivery_preference,
resend: resend,
telephony_response: telephony_response,
adapter: adapter,
success: success,
**extra,
},
Expand Down
28 changes: 19 additions & 9 deletions app/services/encrypted_document_storage/document_writer.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
module EncryptedDocumentStorage
class DocumentWriter
def encrypt_and_write_document(front_image:, back_image:)
def encrypt_and_write_document(
front_image:,
front_image_content_type:,
back_image:,
back_image_content_type:
)
key = SecureRandom.bytes(32)
encrypted_front_image = aes_cipher.encrypt(front_image, key)
encrypted_back_image = aes_cipher.encrypt(back_image, key)

front_image_uuid = SecureRandom.uuid
back_image_uiid = SecureRandom.uuid
front_filename = build_filename_for_content_type(front_image_content_type)
back_filename = build_filename_for_content_type(back_image_content_type)

storage.write_image(encrypted_image: encrypted_front_image, name: front_image_uuid)
storage.write_image(encrypted_image: encrypted_back_image, name: back_image_uiid)
storage.write_image(encrypted_image: encrypted_front_image, name: front_filename)
storage.write_image(encrypted_image: encrypted_back_image, name: back_filename)

WriteDocumentResult.new(
front_uuid: front_image_uuid,
back_uuid: back_image_uiid,
front_encryption_key: Base64.strict_encode64(key),
back_encryption_key: Base64.strict_encode64(key),
front_filename: front_filename,
back_filename: back_filename,
encryption_key: Base64.strict_encode64(key),
)
end

Expand All @@ -32,5 +36,11 @@ def storage
def aes_cipher
@aes_cipher ||= Encryption::AesCipher.new
end

# @return [String] A new, unique S3 key for an image of the given content type.
def build_filename_for_content_type(content_type)
ext = Rack::Mime::MIME_TYPES.rassoc(content_type)&.first
"#{SecureRandom.uuid}#{ext}"
end
end
end
6 changes: 6 additions & 0 deletions app/services/encrypted_document_storage/local_storage.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module EncryptedDocumentStorage
class LocalStorage
# Used in tests to verify results
def read_image(name:)
filepath = tmp_document_storage_dir.join(name)
File.read(filepath)
end

def write_image(encrypted_image:, name:)
FileUtils.mkdir_p(tmp_document_storage_dir)
filepath = tmp_document_storage_dir.join(name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
module EncryptedDocumentStorage
WriteDocumentResult = Struct.new(
:front_uuid,
:back_uuid,
:front_encryption_key,
:back_encryption_key,
:front_filename,
:back_filename,
:encryption_key,
keyword_init: true,
)
end
2 changes: 2 additions & 0 deletions app/services/flow/base_flow.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Flow
class BaseFlow
include Failure

attr_accessor :flow_session
attr_reader :steps, :actions, :current_user, :current_sp, :params, :request, :json,
:http_status, :controller
Expand Down
8 changes: 1 addition & 7 deletions app/services/flow/base_step.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Flow
class BaseStep
include Rails.application.routes.url_helpers
include Failure

def initialize(flow, name)
@flow = flow
Expand Down Expand Up @@ -51,13 +52,6 @@ def form_submit
FormResponse.new(success: true)
end

def failure(message, extra = nil)
flow_session[:error_message] = message
form_response_params = { success: false, errors: { message: message } }
form_response_params[:extra] = extra unless extra.nil?
FormResponse.new(**form_response_params)
end

def flow_params
params[@name]
end
Expand Down
12 changes: 12 additions & 0 deletions app/services/flow/failure.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Flow
module Failure
private

def failure(message, extra = nil)
flow_session[:error_message] = message
form_response_params = { success: false, errors: { message: message } }
form_response_params[:extra] = extra unless extra.nil?
FormResponse.new(**form_response_params)
end
end
end
Loading