diff --git a/app/forms/idv/api_image_upload_form.rb b/app/forms/idv/api_image_upload_form.rb index 1013d36387d..e93651a7176 100644 --- a/app/forms/idv/api_image_upload_form.rb +++ b/app/forms/idv/api_image_upload_form.rb @@ -2,6 +2,7 @@ module Idv class ApiImageUploadForm include ActiveModel::Model include ActionView::Helpers::TranslationHelper + include ApplicationHelper validates_presence_of :front validates_presence_of :back @@ -459,14 +460,14 @@ def store_failed_images(client_response, doc_pii_response) front_image_fingerprint: failed_front_fingerprint, back_image_fingerprint: failed_back_fingerprint, doc_auth_success: client_response.doc_auth_success?, - selfie_success: client_response.selfie_success, + selfie_status: selfie_status_from_response(client_response), ) elsif doc_pii_response && !doc_pii_response.success? document_capture_session.store_failed_auth_data( front_image_fingerprint: extra_attributes[:front_image_fingerprint], back_image_fingerprint: extra_attributes[:back_image_fingerprint], doc_auth_success: client_response.doc_auth_success?, - selfie_success: client_response.selfie_success, + selfie_status: selfie_status_from_response(client_response), ) end # retrieve updated data from session diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7b0eb6c382a..3e434470734 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -60,4 +60,10 @@ def cancel_link_text def desktop_device? !BrowserCache.parse(request.user_agent).mobile? end + + def selfie_status_from_response(client_response) + return client_response.selfie_status if client_response.respond_to?(:selfie_status) + + :not_processed + end end diff --git a/app/models/document_capture_session.rb b/app/models/document_capture_session.rb index d318fffe386..dd36db9aae5 100644 --- a/app/models/document_capture_session.rb +++ b/app/models/document_capture_session.rb @@ -1,5 +1,6 @@ class DocumentCaptureSession < ApplicationRecord include NonNullUuid + include ApplicationHelper belongs_to :user @@ -18,8 +19,7 @@ def store_result_from_response(doc_auth_response) session_result.attention_with_barcode = doc_auth_response.attention_with_barcode? session_result.selfie_check_performed = doc_auth_response.selfie_check_performed? session_result.doc_auth_success = doc_auth_response.doc_auth_success? - # nil(selfie not required) or true/false - session_result.selfie_success = doc_auth_response.selfie_success + session_result.selfie_status = selfie_status_from_response(doc_auth_response) EncryptedRedisStructStorage.store( session_result, expires_in: IdentityConfig.store.doc_capture_request_valid_for_minutes.minutes.seconds.to_i, @@ -29,14 +29,14 @@ def store_result_from_response(doc_auth_response) end def store_failed_auth_data(front_image_fingerprint:, back_image_fingerprint:, doc_auth_success:, - selfie_success:) + selfie_status:) session_result = load_result || DocumentCaptureSessionResult.new( id: generate_result_id, ) session_result.success = false session_result.captured_at = Time.zone.now session_result.doc_auth_success = doc_auth_success - session_result.selfie_success = selfie_success + session_result.selfie_status = selfie_status session_result.add_failed_front_image!(front_image_fingerprint) if front_image_fingerprint session_result.add_failed_back_image!(back_image_fingerprint) if back_image_fingerprint EncryptedRedisStructStorage.store( diff --git a/app/services/doc_auth/acuant/responses/get_results_response.rb b/app/services/doc_auth/acuant/responses/get_results_response.rb index e6b64f3ebaf..f66393da95d 100644 --- a/app/services/doc_auth/acuant/responses/get_results_response.rb +++ b/app/services/doc_auth/acuant/responses/get_results_response.rb @@ -73,7 +73,7 @@ def create_response_info classification_info: classification_info, address_line2_present: !pii_from_doc[:address2].blank?, doc_auth_success: doc_auth_success?, - selfie_success: nil, + selfie_status: :not_processed, } end diff --git a/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb b/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb index 731a7c69aa6..d117fa72582 100644 --- a/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb +++ b/app/services/doc_auth/lexis_nexis/responses/true_id_response.rb @@ -150,14 +150,18 @@ def doc_auth_success? doc_auth_result_passed? end - # @return [Boolean, nil] - # When selfie result is missing, return nil + # @return [:success, :fail, :not_processed] + # When selfie result is missing, return :not_processed # Otherwise: - # return true if selfie check result == 'Pass' - # return false - def selfie_success - return selfie_result if selfie_result.nil? - selfie_result == 'Pass' + # return :success if selfie check result == 'Pass' + # return :fail + def selfie_status + return :not_processed if selfie_result.nil? + selfie_result == 'Pass' ? :success : :fail + end + + def selfie_passed? + selfie_status == :success end private @@ -248,7 +252,7 @@ def all_passed? true_id_product.present? && product_status_passed? && doc_auth_result_passed? && - (@liveness_checking_enabled ? selfie_success : true) + (@liveness_checking_enabled ? selfie_passed? : true) end def selfie_result diff --git a/app/services/doc_auth/mock/result_response.rb b/app/services/doc_auth/mock/result_response.rb index 43efae7283e..ccc79ed34f9 100644 --- a/app/services/doc_auth/mock/result_response.rb +++ b/app/services/doc_auth/mock/result_response.rb @@ -9,8 +9,8 @@ class ResultResponse < DocAuth::Response def initialize(uploaded_file, selfie_check_performed, config) @uploaded_file = uploaded_file.to_s - @config = config @selfie_check_performed = selfie_check_performed + @config = config super( success: success?, errors: errors, @@ -136,9 +136,9 @@ def doc_auth_success? doc_auth_result_from_uploaded_file == 'Passed' || errors.blank? end - def selfie_success - return nil if portrait_match_results&.dig(:FaceMatchResult).nil? - portrait_match_results[:FaceMatchResult] == 'Pass' + def selfie_status + return :not_processed if portrait_match_results&.dig(:FaceMatchResult).nil? + portrait_match_results[:FaceMatchResult] == 'Pass' ? :success : :fail end private @@ -183,7 +183,11 @@ def doc_auth_result_from_success def all_doc_capture_values_passing?(doc_auth_result, id_type_supported) doc_auth_result == 'Passed' && id_type_supported && - (@selfie_check_performed ? selfie_success : true) + (@selfie_check_performed ? selfie_passed? : true) + end + + def selfie_passed? + selfie_status == :success end def parse_uri diff --git a/app/services/doc_auth/response.rb b/app/services/doc_auth/response.rb index c14323f8a05..d5e7506688c 100644 --- a/app/services/doc_auth/response.rb +++ b/app/services/doc_auth/response.rb @@ -1,7 +1,9 @@ module DocAuth class Response - attr_reader :errors, :exception, :extra, :pii_from_doc, :doc_type_supported, :selfie_live, - :selfie_quality_good + include ApplicationHelper + + attr_reader :errors, :exception, :extra, :pii_from_doc, :doc_type_supported, + :selfie_live, :selfie_quality_good ID_TYPE_SLUGS = { 'Identification Card' => 'state_id_card', @@ -74,7 +76,7 @@ def to_h selfie_live: selfie_live?, selfie_quality_good: selfie_quality_good?, doc_auth_success: doc_auth_success?, - selfie_success: selfie_success, + selfie_status: selfie_status_from_response(self), }.merge(extra) end @@ -97,10 +99,6 @@ def selfie_check_performed? @selfie_check_performed end - def selfie_success - # to be implemented by concrete subclass - end - def doc_auth_success? # to be implemented by concrete subclass false diff --git a/app/services/document_capture_session_result.rb b/app/services/document_capture_session_result.rb index 9b9f0f560bd..61257d88631 100644 --- a/app/services/document_capture_session_result.rb +++ b/app/services/document_capture_session_result.rb @@ -10,16 +10,20 @@ :failed_back_image_fingerprints, :captured_at, :selfie_check_performed, - :doc_auth_success, :selfie_success, + :doc_auth_success, :selfie_status, :selfie_success, keyword_init: true, allowed_members: [:id, :success, :attention_with_barcode, :failed_front_image_fingerprints, :failed_back_image_fingerprints, :captured_at, :selfie_check_performed, - :doc_auth_success, :selfie_success] + :doc_auth_success, :selfie_status, :selfie_success] ) do def self.redis_key_prefix 'dcs:result' end + def selfie_status + self[:selfie_status].to_sym + end + alias_method :success?, :success alias_method :attention_with_barcode?, :attention_with_barcode alias_method :pii_from_doc, :pii diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb index cc6bed5f6c1..cce66a44b26 100644 --- a/spec/controllers/idv/image_uploads_controller_spec.rb +++ b/spec/controllers/idv/image_uploads_controller_spec.rb @@ -438,7 +438,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: boolean, selfie_quality_good: boolean, @@ -621,7 +621,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: true, selfie_quality_good: true, @@ -717,7 +717,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: true, selfie_quality_good: true, @@ -813,7 +813,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: true, selfie_quality_good: true, @@ -906,7 +906,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: true, selfie_quality_good: true, @@ -1024,8 +1024,8 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, + selfie_status: :not_processed, liveness_checking_required: boolean, - selfie_success: nil, selfie_live: true, selfie_quality_good: true, ) @@ -1100,7 +1100,7 @@ back_image_fingerprint: an_instance_of(String), doc_type_supported: boolean, doc_auth_success: boolean, - selfie_success: nil, + selfie_status: :not_processed, liveness_checking_required: boolean, selfie_live: boolean, selfie_quality_good: boolean, diff --git a/spec/forms/idv/api_image_upload_form_spec.rb b/spec/forms/idv/api_image_upload_form_spec.rb index c7f1e1006b7..cfd292b4090 100644 --- a/spec/forms/idv/api_image_upload_form_spec.rb +++ b/spec/forms/idv/api_image_upload_form_spec.rb @@ -164,7 +164,7 @@ selfie_live: boolean, selfie_quality_good: boolean, doc_auth_success: boolean, - selfie_success: anything, + selfie_status: anything, ) end @@ -491,7 +491,6 @@ allow(client_response).to receive(:network_error?).and_return(false) allow(client_response).to receive(:errors).and_return(errors) allow(client_response).to receive(:doc_auth_success?).and_return(false) - allow(client_response).to receive(:selfie_success).and_return(nil) form.send(:validate_form) capture_result = form.send(:store_failed_images, client_response, doc_pii_response) expect(capture_result[:front]).not_to be_empty @@ -505,7 +504,6 @@ allow(client_response).to receive(:network_error?).and_return(false) allow(client_response).to receive(:errors).and_return(errors) allow(client_response).to receive(:doc_auth_success?).and_return(false) - allow(client_response).to receive(:selfie_success).and_return(nil) form.send(:validate_form) capture_result = form.send(:store_failed_images, client_response, doc_pii_response) expect(capture_result[:front]).not_to be_empty @@ -519,7 +517,6 @@ allow(client_response).to receive(:network_error?).and_return(false) allow(client_response).to receive(:errors).and_return(errors) allow(client_response).to receive(:doc_auth_success?).and_return(false) - allow(client_response).to receive(:selfie_success).and_return(nil) form.send(:validate_form) capture_result = form.send(:store_failed_images, client_response, doc_pii_response) expect(capture_result[:front]).not_to be_empty @@ -548,7 +545,6 @@ allow(client_response).to receive(:network_error?).and_return(true) allow(client_response).to receive(:errors).and_return(errors) allow(client_response).to receive(:doc_auth_success?).and_return(false) - allow(client_response).to receive(:selfie_success).and_return(nil) allow(doc_pii_response).to receive(:success?).and_return(false) form.send(:validate_form) capture_result = form.send(:store_failed_images, client_response, doc_pii_response) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 6f8fa036d4f..8fae8f15415 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -96,4 +96,27 @@ end end end + + describe '#selfie_status_from_response' do + context 'the response does not have selfie_status method defined on it' do + let(:success) { true } + let(:errors) { {} } + let(:exception) { nil } + let(:pii_from_doc) { {} } + let(:attention_with_barcode) { false } + let(:response) do + DocAuth::Response.new( + success: success, + errors: errors, + exception: exception, + pii_from_doc: pii_from_doc, + attention_with_barcode: attention_with_barcode, + ) + end + + it 'returns :not_processed' do + expect(selfie_status_from_response(response)).to eq(:not_processed) + end + end + end end diff --git a/spec/models/document_capture_session_spec.rb b/spec/models/document_capture_session_spec.rb index f2de75d0462..406a02a7929 100644 --- a/spec/models/document_capture_session_spec.rb +++ b/spec/models/document_capture_session_spec.rb @@ -96,7 +96,7 @@ front_image_fingerprint: 'fingerprint1', back_image_fingerprint: nil, doc_auth_success: false, - selfie_success: nil, + selfie_status: :not_processed, ) result_id = record.result_id key = EncryptedRedisStructStorage.key(result_id, type: DocumentCaptureSessionResult) @@ -107,7 +107,7 @@ expect(result.failed_front_image?(nil)).to eq(false) expect(result.failed_back_image?(nil)).to eq(false) expect(result.doc_auth_success).to eq(false) - expect(result.selfie_success).to be_nil + expect(result.selfie_status).to eq(:not_processed) end it 'saves failed image finterprints' do @@ -117,7 +117,7 @@ front_image_fingerprint: 'fingerprint1', back_image_fingerprint: nil, doc_auth_success: false, - selfie_success: nil, + selfie_status: :not_processed, ) old_result = record.load_result @@ -125,7 +125,7 @@ front_image_fingerprint: 'fingerprint2', back_image_fingerprint: 'fingerprint3', doc_auth_success: false, - selfie_success: nil, + selfie_status: :not_processed, ) new_result = record.load_result @@ -133,13 +133,13 @@ expect(old_result.failed_front_image?('fingerprint2')).to eq(false) expect(old_result.failed_back_image?('fingerprint3')).to eq(false) expect(old_result.doc_auth_success).to eq(false) - expect(old_result.selfie_success).to be_nil + expect(old_result.selfie_status).to eq(:not_processed) expect(new_result.failed_front_image?('fingerprint1')).to eq(true) expect(new_result.failed_front_image?('fingerprint2')).to eq(true) expect(new_result.failed_back_image?('fingerprint3')).to eq(true) expect(new_result.doc_auth_success).to eq(false) - expect(new_result.selfie_success).to be_nil + expect(new_result.selfie_status).to eq(:not_processed) end end end diff --git a/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb b/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb index 66890bdf77c..1fc657f2e15 100644 --- a/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb +++ b/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb @@ -63,7 +63,7 @@ address_line2_present: true, doc_type_supported: true, doc_auth_success: true, - selfie_success: nil, + selfie_status: :not_processed, selfie_live: true, selfie_quality_good: true, } @@ -423,7 +423,7 @@ address_line2_present: true, doc_type_supported: false, doc_auth_success: true, - selfie_success: nil, + selfie_status: :not_processed, selfie_live: true, selfie_quality_good: true, } @@ -508,7 +508,7 @@ address_line2_present: true, doc_type_supported: false, doc_auth_success: true, - selfie_success: nil, + selfie_status: :not_processed, selfie_live: true, selfie_quality_good: true, } diff --git a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb index 7aa64c144c7..d71d3005ba8 100644 --- a/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb +++ b/spec/services/doc_auth/lexis_nexis/responses/true_id_response_spec.rb @@ -134,7 +134,7 @@ Back: a_hash_including(:ClassName, :CountryCode, :IssuerType), }, doc_auth_success: true, - selfie_success: nil, + selfie_status: :not_processed, selfie_live: true, selfie_quality_good: true, ) @@ -321,7 +321,7 @@ def get_decision_product(resp) output = response.to_h expect(output[:success]).to eq(false) expect(response.doc_auth_success?).to eq(false) - expect(response.selfie_success).to eq(false) + expect(response.selfie_status).to eq(:fail) end it 'produces expected hash output' do @@ -377,7 +377,7 @@ def get_decision_product(resp) Back: a_hash_including(:ClassName, :CountryCode, :IssuerType), }, doc_auth_success: false, - selfie_success: false, + selfie_status: :fail, selfie_live: true, selfie_quality_good: false, ) @@ -654,31 +654,31 @@ def get_decision_product(resp) end end - describe '#selfie_success' do + describe '#selfie_status' do context 'when selfie check is disabled' do let(:response) { described_class.new(success_response, config, false) } - it 'returns nil' do - expect(response.selfie_success).to eq(nil) + it 'returns :not_processed' do + expect(response.selfie_status).to eq(:not_processed) end end context 'when selfie check is enabled' do context 'whe missing selfie result in response' do let(:response) { described_class.new(success_response, config, true) } - it 'returns nil when missing selfie in response' do - expect(response.selfie_success).to eq(nil) + it 'returns :not_processed when missing selfie in response' do + expect(response.selfie_status).to eq(:not_processed) end end context 'when selfie passed' do let(:response) { described_class.new(success_with_liveness_response, config, true) } - it 'returns true' do - expect(response.selfie_success).to eq(true) + it 'returns :success' do + expect(response.selfie_status).to eq(:success) end end context 'when selfie failed' do let(:response) { described_class.new(failure_response_with_liveness, config, true) } - it 'returns false' do - expect(response.selfie_success).to eq(false) + it 'returns :fail' do + expect(response.selfie_status).to eq(:fail) end end end diff --git a/spec/services/doc_auth/mock/result_response_spec.rb b/spec/services/doc_auth/mock/result_response_spec.rb index 8e1448a8a45..06b40ddb8c4 100644 --- a/spec/services/doc_auth/mock/result_response_spec.rb +++ b/spec/services/doc_auth/mock/result_response_spec.rb @@ -305,7 +305,7 @@ classification_info: {}, ) expect(response.doc_auth_success?).to eq(true) - expect(response.selfie_success).to be_nil + expect(response.selfie_status).to eq(:not_processed) end end @@ -672,7 +672,7 @@ expect(response.success?).to eq(true) expect(response.extra[:portrait_match_results]).to eq(selfie_results) expect(response.doc_auth_success?).to eq(true) - expect(response.selfie_success).to eq(true) + expect(response.selfie_status).to eq(:success) end end @@ -699,7 +699,7 @@ expect(response.success?).to eq(false) expect(response.extra[:portrait_match_results]).to eq(selfie_results) expect(response.doc_auth_success?).to eq(true) - expect(response.selfie_success).to eq(false) + expect(response.selfie_status).to eq(:fail) end end end @@ -712,7 +712,7 @@ expect(response.selfie_check_performed?).to eq(false) expect(response.extra).not_to have_key(:portrait_match_results) expect(response.doc_auth_success?).to eq(true) - expect(response.selfie_success).to be_nil + expect(response.selfie_status).to eq(:not_processed) end end end diff --git a/spec/services/document_capture_session_result_spec.rb b/spec/services/document_capture_session_result_spec.rb index 308c729e019..b2c1770c464 100644 --- a/spec/services/document_capture_session_result_spec.rb +++ b/spec/services/document_capture_session_result_spec.rb @@ -35,5 +35,17 @@ expect(result.failed_front_image?(nil)).to eq(false) expect(result.failed_back_image?(nil)).to eq(false) end + describe '#selfie_status' do + it 'returns a symbol' do + result = DocumentCaptureSessionResult.new( + id: id, + success: success, + pii: pii, + attention_with_barcode: false, + selfie_status: 'success', + ) + expect(result.selfie_status).to be_an_instance_of(Symbol) + end + end end end