From 0085f99e0235c11e9bfeff9a0f2a0160a90b0dd6 Mon Sep 17 00:00:00 2001
From: tsoganov
+Anname teada, et järgmise kontakti kinnitamisprotsess on edukalt lõpule viidud.
+
+Täielikud tulemused leiate manuses olevast PDF-failist.
+
+Kontaktandmed:
+
+
+
+Parimate soovidega, +
+<%= render 'mailers/shared/signatures/signature.et.html' %> ++We are writing to inform you that the verification process for the following contact has been successfully completed. +
++The full result can be found in the attached PDF file. +
++If you have any questions regarding the contact verification or other services in the Registrar Portal, please do not hesitate to reach out to us. +
++Best regards, +
+<%= render 'mailers/shared/signatures/signature.en.html' %> \ No newline at end of file diff --git a/app/views/mailers/registrar_mailer/contact_verified.text.erb b/app/views/mailers/registrar_mailer/contact_verified.text.erb new file mode 100644 index 000000000..17cb6e323 --- /dev/null +++ b/app/views/mailers/registrar_mailer/contact_verified.text.erb @@ -0,0 +1,32 @@ +Tere, + +Anname teada, et järgmise kontakti kinnitamisprotsess on edukalt lõpule viidud. + +Kontaktandmed: +- Kood: <%= @contact.code %> +- Nimi: <%= @contact.name %> +- Identifikaator: <%= ident_for(@contact) %> + +Täielikke tulemusi saab vaadata kontakti lehelt registripidaja portaalis. + +Kui Teil on küsimusi seoses kontakti kinnitamise või muude teenustega registripidaja portaalis, võtke meiega ühendust. + +Parimate soovidega, +<%= render 'mailers/shared/signatures/signature.et.text' %> +--- + +Hi, + +We are writing to inform you that the verification process for the following contact has been successfully completed. + +Contact Details: +- Code: <%= @contact.code %> +- Name: <%= @contact.name %> +- Ident: <%= ident_for(@contact) %> + +The full result can be viewed on the contact page in the Registrar Portal. + +If you have any questions regarding the contact verification or other services in the Registrar Portal, please do not hesitate to reach out to us. + +Best regards, +<%= render 'mailers/shared/signatures/signature.en.text' %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 2b12f0fda..3a1c538a8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -75,6 +75,7 @@ Rails.application.routes.draw do get 'check/:id', to: 'contacts#check' get 'search(/:id)', to: 'contacts#search' post 'verify/:id', to: 'contacts#verify' + get 'download_poi/:id', to: 'contacts#download_poi' end end end diff --git a/db/migrate/20241015071505_add_verification_id_to_contacts.rb b/db/migrate/20241015071505_add_verification_id_to_contacts.rb new file mode 100644 index 000000000..7aff96abc --- /dev/null +++ b/db/migrate/20241015071505_add_verification_id_to_contacts.rb @@ -0,0 +1,5 @@ +class AddVerificationIdToContacts < ActiveRecord::Migration[6.1] + def change + add_column :contacts, :verification_id, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index 82f10a207..d73bf3ebe 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -696,7 +696,8 @@ CREATE TABLE public.contacts ( checked_company_at timestamp without time zone, company_register_status character varying ident_request_sent_at timestamp without time zone, - verified_at timestamp without time zone + verified_at timestamp without time zone, + verification_id character varying ); @@ -5613,6 +5614,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240816091049'), ('20240816092636'), ('20240903131540'), -('20240924103554'); +('20240924103554'), +('20241015071505'); diff --git a/test/integration/eeid/identification_requests_webhook_test.rb b/test/integration/eeid/identification_requests_webhook_test.rb index 26bbac6cd..625cfa3a5 100644 --- a/test/integration/eeid/identification_requests_webhook_test.rb +++ b/test/integration/eeid/identification_requests_webhook_test.rb @@ -11,6 +11,15 @@ class Eeid::IdentificationRequestsWebhookTest < ActionDispatch::IntegrationTest } @valid_hmac_signature = OpenSSL::HMAC.hexdigest('SHA256', @secret, payload.to_json) + stub_request(:post, %r{api/auth/v1/token}) + .to_return( + status: 200, + body: { access_token: 'token', token_type: 'Bearer', expires_in: 100 }.to_json, headers: {} + ) + pdf_content = File.read(Rails.root.join('test/fixtures/files/legaldoc.pdf')) + stub_request(:get, %r{api/ident/v1/identification_requests}) + .to_return(status: 200, body: pdf_content, headers: { 'Content-Type' => 'application/pdf' }) + adapter = ENV['shunter_default_adapter'].constantize.new adapter&.clear! end @@ -22,6 +31,8 @@ class Eeid::IdentificationRequestsWebhookTest < ActionDispatch::IntegrationTest assert_response :ok assert_equal({ 'status' => 'success' }, JSON.parse(response.body)) assert_not_nil @contact.reload.verified_at + assert_equal @contact.verification_id, '123' + assert_notify_registrar('Successful Contact Verification') end test 'should return unauthorized for invalid HMAC signature' do @@ -29,13 +40,15 @@ class Eeid::IdentificationRequestsWebhookTest < ActionDispatch::IntegrationTest assert_response :unauthorized assert_equal({ 'error' => 'Invalid HMAC signature' }, JSON.parse(response.body)) + assert_emails 0 end test 'should return unauthorized for missing parameters' do post '/eeid/webhooks/identification_requests', params: { reference: @contact.code }, as: :json, headers: { 'X-HMAC-Signature' => @valid_hmac_signature } assert_response :unauthorized - assert_equal({ 'error' => 'Invalid HMAC signature' }, JSON.parse(response.body)) + assert_equal({ 'error' => 'Invalid HMAC signature' }, JSON.parse(response.body)) + assert_emails 0 end test 'should handle internal server error gracefully' do @@ -44,10 +57,24 @@ class Eeid::IdentificationRequestsWebhookTest < ActionDispatch::IntegrationTest post '/eeid/webhooks/identification_requests', params: { identification_request_id: '123', reference: @contact.code }, as: :json, headers: { 'X-HMAC-Signature' => @valid_hmac_signature } assert_response :internal_server_error - assert_equal({ 'error' => 'Internal Server Error' }, JSON.parse(response.body)) + assert_equal({ 'error' => 'Simulated error' }, JSON.parse(response.body)) + assert_emails 0 end end + test 'should handle error from ident response' do + stub_request(:get, %r{api/ident/v1/identification_requests}) + .to_return(status: :not_found, body: { error: 'Proof of identity not found' }.to_json, headers: { 'Content-Type' => 'application/json' }) + + @contact.update!(ident_request_sent_at: Time.zone.now - 1.day) + post '/eeid/webhooks/identification_requests', params: { identification_request_id: '123', reference: @contact.code }, as: :json, headers: { 'X-HMAC-Signature' => @valid_hmac_signature } + + assert_response :internal_server_error + assert_equal({ 'error' => 'Proof of identity not found' }, JSON.parse(response.body)) + assert_emails 0 + assert_nil @contact.reload.verified_at + end + test 'returns error response if throttled' do ENV['shunter_default_threshold'] = '1' ENV['shunter_enabled'] = 'true' @@ -60,4 +87,15 @@ class Eeid::IdentificationRequestsWebhookTest < ActionDispatch::IntegrationTest ENV['shunter_default_threshold'] = '10000' ENV['shunter_enabled'] = 'false' end + + private + + def assert_notify_registrar(subject) + assert_emails 1 + email = ActionMailer::Base.deliveries.last + assert_equal [@contact.registrar.email], email.to + assert_equal subject, email.subject + assert_equal 1, email.attachments.size + assert_equal 'proof_of_identity.pdf', email.attachments.first.filename + end end diff --git a/test/integration/repp/v1/contacts/download_poi_test.rb b/test/integration/repp/v1/contacts/download_poi_test.rb new file mode 100644 index 000000000..1d943dc5c --- /dev/null +++ b/test/integration/repp/v1/contacts/download_poi_test.rb @@ -0,0 +1,72 @@ +require 'test_helper' + +class ReppV1ContactsDownloadPoiTest < ActionDispatch::IntegrationTest + def setup + @contact = contacts(:john) + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + + adapter = ENV['shunter_default_adapter'].constantize.new + adapter&.clear! + + stub_request(:post, %r{api/auth/v1/token}) + .to_return( + status: 200, + body: { access_token: 'token', token_type: 'Bearer', expires_in: 100 }.to_json, headers: {} + ) + pdf_content = File.read(Rails.root.join('test/fixtures/files/legaldoc.pdf')) + stub_request(:get, %r{api/ident/v1/identification_requests}) + .to_return(status: 200, body: pdf_content, headers: { 'Content-Type' => 'application/pdf' }) + end + + def test_returns_error_when_not_found + get '/repp/v1/contacts/download_poi/nonexistant:code', headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :not_found + assert_equal 2303, json[:code] + assert_equal 'Object does not exist', json[:message] + end + + def test_downloads_poi_for_contact + @contact.update!(verified_at: Time.zone.now - 1.day, verification_id: '123') + get "/repp/v1/contacts/download_poi/#{@contact.code}", headers: @auth_headers + + assert_response :ok + assert_equal 'application/pdf', response.headers['Content-Type'] + assert_equal "inline; filename=\"proof_of_identity_123.pdf\"; filename*=UTF-8''proof_of_identity_123.pdf", response.headers['Content-Disposition'] + assert_not_empty response.body + end + + def test_handles_non_epp_error + stub_request(:get, %r{api/ident/v1/identification_requests}) + .to_return( + status: :not_found, + body: { error: 'Proof of identity not found' }.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + get "/repp/v1/contacts/download_poi/#{@contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 'Proof of identity not found', json[:message] + end + + def test_returns_error_response_if_throttled + ENV['shunter_default_threshold'] = '1' + ENV['shunter_enabled'] = 'true' + + get "/repp/v1/contacts/download_poi/#{@contact.code}", headers: @auth_headers + get "/repp/v1/contacts/download_poi/#{@contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal json[:code], 2502 + assert response.body.include?(Shunter.default_error_message) + ENV['shunter_default_threshold'] = '10000' + ENV['shunter_enabled'] = 'false' + end +end diff --git a/test/integration/repp/v1/contacts/verify_test.rb b/test/integration/repp/v1/contacts/verify_test.rb index 3a48b4552..1d62c4b83 100644 --- a/test/integration/repp/v1/contacts/verify_test.rb +++ b/test/integration/repp/v1/contacts/verify_test.rb @@ -12,14 +12,18 @@ class ReppV1ContactsVerifyTest < ActionDispatch::IntegrationTest adapter = ENV['shunter_default_adapter'].constantize.new adapter&.clear! - stub_request(:post, %r{api/auth/v1/token}).to_return(status: 200, body: { access_token: 'token', token_type: 'Bearer', expires_in: 100 }.to_json, headers: {}) + stub_request(:post, %r{api/auth/v1/token}) + .to_return( + status: 200, + body: { access_token: 'token', token_type: 'Bearer', expires_in: 100 }.to_json, headers: {} + ) stub_request(:post, %r{api/ident/v1/identification_requests}) .with( body: { claims_required: [{ type: 'sub', value: "#{@contact.ident_country_code}#{@contact.ident}" }], reference: @contact.code } - ).to_return(status: 200, body: { id: '123' }.to_json, headers: {}) + ).to_return(status: 200, body: { id: '123' }.to_json, headers: { 'Content-Type' => 'application/json' }) end def test_returns_error_when_not_found @@ -43,6 +47,20 @@ class ReppV1ContactsVerifyTest < ActionDispatch::IntegrationTest assert contact.present? assert contact.ident_request_sent_at assert_nil contact.verified_at + assert_notify_contact('Identification requested') + end + + def test_handles_non_epp_error + stub_request(:post, %r{api/ident/v1/identification_requests}) + .to_return(status: :unprocessable_entity, body: { error: 'error' }.to_json, headers: { 'Content-Type' => 'application/json' }) + + post "/repp/v1/contacts/verify/#{@contact.code}", headers: @auth_headers + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :bad_request + assert_equal 'Sending identification request failed', json[:message] + assert_nil @contact.ident_request_sent_at + assert_emails 0 end def test_does_not_verify_already_verified_contact @@ -68,4 +86,13 @@ class ReppV1ContactsVerifyTest < ActionDispatch::IntegrationTest ENV['shunter_default_threshold'] = '10000' ENV['shunter_enabled'] = 'false' end + + private + + def assert_notify_contact(subject) + assert_emails 1 + email = ActionMailer::Base.deliveries.last + assert_equal [@contact.email], email.to + assert_equal subject, email.subject + end end