From 45d84d2197f50efb714a25c2caca3c9ad9612cf7 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Fri, 8 Nov 2024 21:41:23 -0500 Subject: [PATCH 1/3] rebased from main --- app/controllers/socure_webhook_controller.rb | 14 +- app/services/analytics_events.rb | 2 + .../socure_webhook_controller_spec.rb | 329 ++++++++---------- spec/factories/document_capture_sessions.rb | 11 + 4 files changed, 167 insertions(+), 189 deletions(-) create mode 100644 spec/factories/document_capture_sessions.rb diff --git a/app/controllers/socure_webhook_controller.rb b/app/controllers/socure_webhook_controller.rb index a4ba596cae3..1deb4a46e51 100644 --- a/app/controllers/socure_webhook_controller.rb +++ b/app/controllers/socure_webhook_controller.rb @@ -80,9 +80,10 @@ def log_webhook_receipt analytics.idv_doc_auth_socure_webhook_received( created_at: event[:created], customer_user_id: event[:customerUserId], + docv_transaction_token: event[:docvTransactionToken], event_type: event[:eventType], reference_id: event[:referenceId], - user_id: event[:customerUserId], + user_id: user&.uuid, ) end @@ -94,9 +95,8 @@ def increment_rate_limiter end def document_capture_session - token = event[:docvTransactionToken] || event[:docVTransactionToken] @document_capture_session ||= DocumentCaptureSession.find_by( - socure_docv_transaction_token: token, + socure_docv_transaction_token: docv_transaction_token, ) end @@ -117,4 +117,12 @@ def socure_params :docvTransactionToken, :docVTransactionToken], ) end + + def user + @user ||= document_capture_session&.user + end + + def docv_transaction_token + @docv_transaction_token ||= event[:docvTransactionToken] || event[:docVTransactionToken] + end end diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index f9a49854a66..7f32e442189 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -1573,6 +1573,7 @@ def idv_doc_auth_socure_webhook_received( created_at:, customer_user_id:, event_type:, + docv_transaction_token:, reference_id:, user_id:, **extra @@ -1581,6 +1582,7 @@ def idv_doc_auth_socure_webhook_received( :idv_doc_auth_socure_webhook_received, created_at:, customer_user_id:, + docv_transaction_token:, event_type:, reference_id:, user_id:, diff --git a/spec/controllers/socure_webhook_controller_spec.rb b/spec/controllers/socure_webhook_controller_spec.rb index 479441dea9e..cc61bf57827 100644 --- a/spec/controllers/socure_webhook_controller_spec.rb +++ b/spec/controllers/socure_webhook_controller_spec.rb @@ -4,24 +4,21 @@ RSpec.describe SocureWebhookController do describe 'POST /api/webhooks/socure/event' do - let(:user) { create(:user) } - let(:socure_docv_transaction_token) { 'dummy_docv_transaction_token' } - let(:document_capture_session) do - DocumentCaptureSession.create(user:).tap do |dcs| - dcs.socure_docv_transaction_token = socure_docv_transaction_token - end - end - let(:rate_limiter) { RateLimiter.new(rate_limit_type: :idv_doc_auth, user: user) } let(:socure_secret_key) { 'this-is-a-secret' } let(:socure_secret_key_queue) { ['this-is-an-old-secret', 'this-is-an-older-secret'] } let(:socure_enabled) { true } + let(:event_type) { 'TEST_WEBHOOK' } + let(:event_docv_transaction_token) { 'TEST_WEBHOOK_TOKEN' } + let(:customer_user_id) { '#1-customer' } + let(:reference_id) { 'the-ref-id' } let(:webhook_body) do { event: { created: '2020-01-01T00:00:00Z', - customerUserId: '123', - eventType: 'TEST_WEBHOOK', - referenceId: 'abc', + customerUserId: customer_user_id, + eventType: event_type, + docvTransactionToken: event_docv_transaction_token, + referenceId: reference_id, data: { documentData: { dob: '2000-01-01', @@ -33,19 +30,6 @@ }, } end - let(:document_uploaded_webhook_body) do - { - eventGroup: 'DocvNotification', - reason: 'DOCUMENTS_UPLOADED', - event: { - created: '2020-01-01T00:00:00Z', - docvTransactionToken: socure_docv_transaction_token, - eventType: 'DOCUMENTS_UPLOADED', - message: 'Documents Upload Successful', - referenceId: user.id, - }, - } - end before do allow(IdentityConfig.store).to receive(:socure_webhook_secret_key). @@ -54,202 +38,175 @@ and_return(socure_secret_key_queue) allow(IdentityConfig.store).to receive(:socure_enabled). and_return(socure_enabled) + allow(SocureDocvResultsJob).to receive(:perform_later) stub_analytics end context 'webhook authentication' do - it 'returns OK and logs an event with a correct secret key and body' do - request.headers['Authorization'] = socure_secret_key - post :create, params: webhook_body - - expect(response).to have_http_status(:ok) - expect(@analytics).to have_logged_event( - :idv_doc_auth_socure_webhook_received, - created_at: '2020-01-01T00:00:00Z', - customer_user_id: '123', - event_type: 'TEST_WEBHOOK', - reference_id: 'abc', - user_id: '123', - ) - end - - it 'returns OK with an older secret key' do - request.headers['Authorization'] = socure_secret_key_queue.last - post :create, params: webhook_body - - expect(response).to have_http_status(:ok) - end - - it 'returns unauthorized with a bad secret key' do - request.headers['Authorization'] = 'ABC123' - post :create, params: webhook_body - - expect(response).to have_http_status(:unauthorized) - end - - it 'returns unauthorized with no secret key' do - post :create, params: webhook_body - - expect(response).to have_http_status(:unauthorized) - end - it 'returns bad request with no event in the body' do request.headers['Authorization'] = socure_secret_key post :create, params: {} expect(response).to have_http_status(:bad_request) end - end - - context 'when DOCUMENTS_UPLOADED event received' do - let(:webhook_body) do - { - id: 'a8202f22-7331-483b-a76a-546f68da062d', - origId: '45ac9531-60ae-4bc7-805e-f7823e4e5545', - eventGroup: 'DocvNotification', - reason: 'DOCUMENTS_UPLOADED', - environmentName: 'Production', - event: { - created: '2024-08-07T21:18:19.949Z', - customerUserId: '111-222-333', - docVTransactionToken: '45ac9531-60ae-4bc7-805e-f7823e4e5545', - eventType: 'DOCUMENTS_UPLOADED', - message: 'Documents Upload Successful', - referenceId: '45ac9531-60ae-4bc7-805e-f7823e4e5545', - userId: '444-555-666', - }, - } - end - it 'returns OK and logs an event with a correct secret key and body' do - request.headers['Authorization'] = socure_secret_key - post :create, params: document_uploaded_webhook_body - expect(response).to have_http_status(:ok) - expect(@analytics).to have_logged_event( - :idv_doc_auth_socure_webhook_received, - created_at: document_uploaded_webhook_body[:event][:created], - event_type: document_uploaded_webhook_body[:event][:eventType], - reference_id: document_uploaded_webhook_body[:event][:referenceId].to_s, - ) - end - - context 'when document capture session exists' do - let(:user) { create(:user) } - let(:document_capture_session) do - DocumentCaptureSession.create(user:).tap do |dcs| - dcs.socure_docv_transaction_token = '45ac9531-60ae-4bc7-805e-f7823e4e5545' - end - end - - before do - request.headers['Authorization'] = socure_secret_key - allow(DocumentCaptureSession).to receive(:find_by). - and_return(document_capture_session) - allow(SocureDocvResultsJob).to receive(:perform_later) - allow(RateLimiter).to receive(:new).with( - { - user: user, - rate_limit_type: :idv_doc_auth, - }, - ).and_return(rate_limiter) - end + context 'received with invalid webhook key' do + it 'returns unauthorized with a bad secret key' do + request.headers['Authorization'] = 'ABC123' + post :create, params: webhook_body - it 'increments rate limiter of correct user' do - expect(rate_limiter.attempts).to eq 0 - post :create, params: document_uploaded_webhook_body - expect(rate_limiter.attempts).to eq 1 - post :create, params: document_uploaded_webhook_body - expect(rate_limiter.attempts).to eq 2 + expect(response).to have_http_status(:unauthorized) end - it 'enqueues a SocureDocvResultsJob' do + it 'returns unauthorized with no secret key' do post :create, params: webhook_body - expect(SocureDocvResultsJob).to have_received(:perform_later). - with(document_capture_session_uuid: document_capture_session.uuid) + expect(response).to have_http_status(:unauthorized) end end - context 'when document capture session does not exist' do + context 'with a valid webhook key' do before do - allow(NewRelic::Agent).to receive(:notice_error) + request.headers['Authorization'] = socure_secret_key end - - it 'logs an error with NewRelic' do - request.headers['Authorization'] = socure_secret_key_queue.last + it 'returns OK and logs an event with a correct secret key and body' do post :create, params: webhook_body - expect(NewRelic::Agent).to have_received(:notice_error) + expect(response).to have_http_status(:ok) + expect(@analytics).to have_logged_event( + :idv_doc_auth_socure_webhook_received, + created_at: '2020-01-01T00:00:00Z', + customer_user_id:, + docv_transaction_token: event_docv_transaction_token, + event_type:, + reference_id: reference_id, + ) end - end - end - - context 'when socure webhook disabled' do - let(:socure_enabled) { false } - it 'the webhook route does not exist' do - request.headers['Authorization'] = socure_secret_key - post :create, params: webhook_body + it 'returns OK with an older secret key' do + request.headers['Authorization'] = socure_secret_key_queue.last + post :create, params: webhook_body - expect(response).to be_not_found - end - end + expect(response).to have_http_status(:ok) + end - context 'when SESSION_COMPLETE event received' do - let(:docv_transaction_token) { '45ac9531-60ae-4bc7-805e-f7823e4e5547' } - let(:webhook_body) do - { - id: 'a8202f22-7331-483b-a76a-546f68da062d', - origId: '45ac9531-60ae-4bc7-805e-f7823e4e5545', - eventGroup: 'DocvNotification', - reason: 'SESSION_COMPLETE', - environmentName: 'Production', - event: { - created: '2024-08-07T21:18:19.949Z', - customerUserId: '111-222-333', - docVTransactionToken: docv_transaction_token, - eventType: 'SESSION_COMPLETE', - message: 'Session Complete', - referenceId: '45ac9531-60ae-4bc7-805e-f7823e4e5545', - }, - } - end + context 'when document capture session exists' do + it 'logs the user\'s uuid' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + post :create, params: webhook_body + + expect(response).to have_http_status(:ok) + expect(@analytics).to have_logged_event( + :idv_doc_auth_socure_webhook_received, + created_at: '2020-01-01T00:00:00Z', + customer_user_id:, + docv_transaction_token: dcs.socure_docv_transaction_token, + event_type:, + reference_id: reference_id, + user_id: dcs.user.uuid, + ) + end - context 'when document capture session exists' do - let(:user) { create(:user) } - let(:document_capture_session) do - DocumentCaptureSession.create(user:).tap do |dcs| - dcs.socure_docv_transaction_token = docv_transaction_token + context 'when DOCUMENTS_UPLOADED event received' do + let(:event_type) { 'DOCUMENTS_UPLOADED' } + + it 'returns OK and logs an event with a correct secret key and body' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + + post :create, params: webhook_body + expect(response).to have_http_status(:ok) + expect(@analytics).to have_logged_event( + :idv_doc_auth_socure_webhook_received, + created_at: webhook_body[:event][:created], + customer_user_id:, + docv_transaction_token: dcs.socure_docv_transaction_token, + event_type:, + reference_id: reference_id, + user_id: dcs.user.uuid, + ) + end + + it 'increments rate limiter of correct user' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + + i = 0 + while i < 4 + rate_limiter = RateLimiter.new( + user: dcs.user, + rate_limit_type: :idv_doc_auth, + ) + expect(rate_limiter.attempts).to eq i + i += 1 + post :create, params: webhook_body + end + end + + it 'enqueues a SocureDocvResultsJob' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + + post :create, params: webhook_body + + expect(SocureDocvResultsJob).to have_received(:perform_later). + with(document_capture_session_uuid: dcs.uuid) + end + + context 'when document capture session does not exist' do + before do + allow(NewRelic::Agent).to receive(:notice_error) + end + + it 'logs an error with NewRelic' do + request.headers['Authorization'] = socure_secret_key_queue.last + post :create, params: webhook_body + + expect(NewRelic::Agent).to have_received(:notice_error) + end + end end - end - before do - request.headers['Authorization'] = socure_secret_key - allow(DocumentCaptureSession).to receive(:find_by). - and_return(document_capture_session) - allow(SocureDocvResultsJob).to receive(:perform_later) - allow(RateLimiter).to receive(:new).with( - { - user: user, - rate_limit_type: :idv_doc_auth, - }, - ).and_return(rate_limiter) - end + context 'when SESSION_COMPLETE event received' do + let(:event_type) { 'SESSION_COMPLETE' } + + it 'does not increment rate limiter of user' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + + i = 0 + while i < 4 + post :create, params: webhook_body + rate_limiter = RateLimiter.new( + user: dcs.user, + rate_limit_type: :idv_doc_auth, + ) + expect(rate_limiter.attempts).to eq 0 + i += 1 + end + end + + it 'does not enqueue a SocureDocvResultsJob' do + dcs = create(:document_capture_session, :socure) + webhook_body[:event][:docvTransactionToken] = dcs.socure_docv_transaction_token + + post :create, params: webhook_body + + expect(SocureDocvResultsJob).not_to have_received(:perform_later) + end + end - it 'does not increment rate limiter of user' do - expect(rate_limiter.attempts).to eq 0 - post :create, params: webhook_body - expect(rate_limiter.attempts).to eq 0 - post :create, params: webhook_body - expect(rate_limiter.attempts).to eq 0 - end + context 'when socure webhook disabled' do + let(:socure_enabled) { false } - it 'does not enqueue a SocureDocvResultsJob' do - post :create, params: webhook_body + it 'the webhook route does not exist' do + post :create, params: webhook_body - expect(SocureDocvResultsJob).not_to have_received(:perform_later). - with(document_capture_session_uuid: document_capture_session.uuid) + expect(response).to be_not_found + end + end end end end diff --git a/spec/factories/document_capture_sessions.rb b/spec/factories/document_capture_sessions.rb new file mode 100644 index 00000000000..679cc9283fd --- /dev/null +++ b/spec/factories/document_capture_sessions.rb @@ -0,0 +1,11 @@ +FactoryBot.define do + factory :document_capture_session do + uuid { SecureRandom.uuid } + user { association :user, :fully_registered } + end + + trait :socure do + socure_docv_transaction_token { SecureRandom.uuid } + socure_docv_capture_app_url { 'https://capture-app.test' } + end +end From a947bb53da037bd543d2053f5e4b7fa92aa8c472 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Fri, 8 Nov 2024 22:21:07 -0500 Subject: [PATCH 2/3] update the doc for the analytics call changelog: Upcoming Features, Document Authentication, Socure webhook event attribute updates --- app/services/analytics_events.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 7f32e442189..240bb5b40ad 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -1566,9 +1566,10 @@ def idv_doc_auth_redo_ssn_submitted( # @param [String] created_at The created timestamp received from Socure # @param [String] customer_user_id The customerUserId received from Socure + # @param [String] docv_transaction_token The docvTransactionToken received from Socure # @param [String] event_type The eventType received from Socure # @param [String] reference_id The referenceId received from Socure - # @param [String] user_id The customerUserId, repackaged as user_id + # @param [String] user_id The uuid of the user using Socure def idv_doc_auth_socure_webhook_received( created_at:, customer_user_id:, From f748595d4ef4b59cf8baa9d5eb4216e80e756f50 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 12 Nov 2024 13:43:17 -0500 Subject: [PATCH 3/3] move empty event webhook test --- .../socure_webhook_controller_spec.rb | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/spec/controllers/socure_webhook_controller_spec.rb b/spec/controllers/socure_webhook_controller_spec.rb index cc61bf57827..b4a02f11955 100644 --- a/spec/controllers/socure_webhook_controller_spec.rb +++ b/spec/controllers/socure_webhook_controller_spec.rb @@ -44,13 +44,6 @@ end context 'webhook authentication' do - it 'returns bad request with no event in the body' do - request.headers['Authorization'] = socure_secret_key - post :create, params: {} - - expect(response).to have_http_status(:bad_request) - end - context 'received with invalid webhook key' do it 'returns unauthorized with a bad secret key' do request.headers['Authorization'] = 'ABC123' @@ -80,7 +73,7 @@ customer_user_id:, docv_transaction_token: event_docv_transaction_token, event_type:, - reference_id: reference_id, + reference_id:, ) end @@ -91,6 +84,14 @@ expect(response).to have_http_status(:ok) end + context 'when an event does not exist in the body' do + it 'returns bad request' do + post :create, params: {} + + expect(response).to have_http_status(:bad_request) + end + end + context 'when document capture session exists' do it 'logs the user\'s uuid' do dcs = create(:document_capture_session, :socure) @@ -104,7 +105,7 @@ customer_user_id:, docv_transaction_token: dcs.socure_docv_transaction_token, event_type:, - reference_id: reference_id, + reference_id:, user_id: dcs.user.uuid, ) end @@ -124,7 +125,7 @@ customer_user_id:, docv_transaction_token: dcs.socure_docv_transaction_token, event_type:, - reference_id: reference_id, + reference_id:, user_id: dcs.user.uuid, ) end