From 59131b6353fd1b2c29a17a4f158e34306dbfcf8f Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Wed, 4 Dec 2024 12:22:50 +0530 Subject: [PATCH] add testcases supporting token rotation for write api; add testcases for header token addition in read api --- app/controllers/application_controller.rb | 2 +- .../v3/systems/activations_controller_spec.rb | 23 ++++++ .../v3/systems/products_controller_spec.rb | 55 ++++++++++++++ .../v3/systems/systems_controller_spec.rb | 19 +++++ .../v4/systems/products_controller_spec.rb | 73 +++++++++++++++++++ 5 files changed, 171 insertions(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bb7504888..63458615a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -24,7 +24,7 @@ def authenticate_system(skip_on_duplicated: false) # that supports this feature. if system_tokens_enabled? && request.headers.key?(SYSTEM_TOKEN_HEADER) @system.update(last_seen_at: Time.zone.now) - headers[SYSTEM_TOKEN_HEADER] = @system.system_token + system_token_header # only update last_seen_at each 3 minutes, # so that a system that calls SCC every second doesn't write + lock the database row elsif !@system.last_seen_at || @system.last_seen_at < 3.minutes.ago diff --git a/spec/requests/api/connect/v3/systems/activations_controller_spec.rb b/spec/requests/api/connect/v3/systems/activations_controller_spec.rb index a4ff0a562..6a62ee2bd 100644 --- a/spec/requests/api/connect/v3/systems/activations_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/activations_controller_spec.rb @@ -69,5 +69,28 @@ expect(system.scc_synced_at).to be_nil end end + + context 'system token header' do + context 'when system token header is present in request' do + let(:token_headers) do + authenticated_headers.merge({ 'System-Token' => 'some_token' }) + end + + it 'sets system token in response headers' do + get url, headers: token_headers + expect(response.code).to eq '200' + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to be_nil + expect(response.headers['System-Token']).not_to be_empty + end + + it 'does not set system token header if no system token header in request' do + get url, headers: authenticated_headers + + expect(response.code).to eq '200' + expect(response.headers).not_to include('System-Token') + end + end + end end end diff --git a/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 457a77d65..9f6c47916 100644 --- a/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -135,6 +135,26 @@ end end + shared_context 'activate with token in request headers' do + let(:payload) do + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + token: regcode + } + end + + before { post url, headers: { 'System-Token' => 'existing_token' }.merge(headers), params: payload } + subject do + Struct.new(:body, :code, :headers).new( + JSON.parse(response.body, symbolize_names: true), + response.status, + response.headers + ) + end + end + context 'unknown subscription' do include_context 'with subscriptions' let(:regcode) { 'NOT-EXISTING-SUBSCRIPTION' } @@ -173,6 +193,17 @@ expect(activation.product).to eq(product) end end + + context 'token update after activation is success' do + let(:subscription) { create :subscription, :with_products } + let(:product) { subscription.products.first } + let(:regcode) { subscription.regcode } + + include_context 'activate with token in request headers' + its(:code) { is_expected.to eq(201) } + its(:headers) { is_expected.to include('System-Token') } + its(:headers['System-Token']) { is_expected.not_to eq('existing_token') } + end end end @@ -225,6 +256,18 @@ its(:body) { is_expected.to eq(serialized_json) } end + describe 'response header should contain token' do + subject { response } + + let(:token_headers) do + headers.merge({ 'System-Token' => 'some_token' }) + end + + before { get url, headers: token_headers, params: payload } + its(:code) { is_expected.to eq('200') } + its(:headers) { is_expected.to include('System-Token') } + end + describe 'response with "-" in version' do subject { response } @@ -339,6 +382,18 @@ system.reload end + it 'calls refresh_system_token after upgrade action when system token header is present' do + put url, headers: headers.merge('System-Token' => 'test_token'), params: payload + expect(response.code).to eq('201') + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('test_token') + end + + it 'No update in token after upgrade action when system token header is absent' do + put url, headers: headers, params: payload + expect(response.code).to eq('201') + expect(response.headers).not_to include('System-Token') + end context 'new product' do its(:code) { is_expected.to eq('201') } its(:body) { is_expected.to eq(serialized_json) } diff --git a/spec/requests/api/connect/v3/systems/systems_controller_spec.rb b/spec/requests/api/connect/v3/systems/systems_controller_spec.rb index 95eede620..91f73d4f4 100644 --- a/spec/requests/api/connect/v3/systems/systems_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/systems_controller_spec.rb @@ -125,6 +125,25 @@ expect(system.reload.system_information_hash[:user_agent]).to be_nil end end + + context 'response header should contain token' do + let(:headers) { auth_header.merge('System-Token': 'existing-token') } + + it 'contains refreshed token in response' do + update_action + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to equal('existing-token') + end + end + + context 'response header should not contain token' do + let(:headers) { auth_header } + + it 'contains refreshed token in response' do + update_action + expect(response.headers).not_to include('System-Token') + end + end end describe '#deregister' do diff --git a/spec/requests/api/connect/v4/systems/products_controller_spec.rb b/spec/requests/api/connect/v4/systems/products_controller_spec.rb index 8bf9a64f4..6f960a941 100644 --- a/spec/requests/api/connect/v4/systems/products_controller_spec.rb +++ b/spec/requests/api/connect/v4/systems/products_controller_spec.rb @@ -140,4 +140,77 @@ end end end + + describe 'system token refresh' do + let(:system) { FactoryBot.create(:system, :with_activated_base_product) } + let(:headers) { auth_header.merge(version_header).merge('System-Token' => 'existing_token') } + + context 'token refresh for destroy action' do + let(:product) { FactoryBot.create(:product, :extension, :with_mirrored_repositories, :activated, system: system) } + let(:payload) { { identifier: product.identifier, version: product.version, arch: product.arch } } + + it 'refreshes system token when System-Token header is present' do + delete connect_systems_products_url, + headers: headers, + params: payload + + expect(response.status).to eq(200) + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('existing_token') + end + + it 'does not refresh token when System-Token header is absent' do + headers_without_token = auth_header.merge(version_header) + expect_any_instance_of(described_class).not_to receive(:refresh_system_token) + + delete connect_systems_products_url, + headers: headers_without_token, + params: payload + + expect(response.status).to eq(200) + expect(response.headers).not_to include('System-Token') + end + end + + context 'token refresh for synchronize action' do + let(:path) { '/connect/systems/products/synchronize' } + + it 'refreshes system token when System-Token header is present' do + params = system.products.map do |product| + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + release_type: product.release_type + } + end + post path, + params: { products: params }, + headers: headers + + expect(response.status).to eq(200) + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('existing_token') + end + + it 'does not refresh token when System-Token header is absent' do + headers_without_token = auth_header.merge(version_header) + + params = system.products.map do |product| + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + release_type: product.release_type + } + end + post path, + params: { products: params }, + headers: headers_without_token + + expect(response.status).to eq(200) + expect(response.headers).not_to include('System-Token') + end + end + end end