From b9920b4e33d624a752452f3497a154bac091174b Mon Sep 17 00:00:00 2001 From: George Date: Fri, 17 Jan 2025 14:46:26 +0000 Subject: [PATCH 1/6] Remove IP check as handled by HAWK --- .../api/activity_stream_controller.rb | 6 ----- spec/controllers/api/activity_stream_spec.rb | 26 ------------------- 2 files changed, 32 deletions(-) diff --git a/app/controllers/api/activity_stream_controller.rb b/app/controllers/api/activity_stream_controller.rb index 5d8f13543..80f455c6c 100644 --- a/app/controllers/api/activity_stream_controller.rb +++ b/app/controllers/api/activity_stream_controller.rb @@ -316,12 +316,6 @@ def to_search_after(object, method) end def authenticate(request) - remote_ips = request.headers['X-Forwarded-For'].gsub(/\s+/, '').split(',') - return [false, 'Connecting from unauthorized IP'] unless remote_ips.length >= 2 - - authorized_ip_addresses = Figaro.env.ACTIVITY_STREAM_IP_WHITELIST.split(',') - return [false, 'Connecting from unauthorized IP'] unless authorized_ip_addresses.include?(remote_ips[-2]) - return [false, 'Authorization header is missing'] unless request.headers.key?('Authorization') parsed_header_array = request.headers['Authorization'].scan(/([a-z]+)="([^"]+)"/) diff --git a/spec/controllers/api/activity_stream_spec.rb b/spec/controllers/api/activity_stream_spec.rb index da8bbb450..c1469ed03 100644 --- a/spec/controllers/api/activity_stream_spec.rb +++ b/spec/controllers/api/activity_stream_spec.rb @@ -36,32 +36,6 @@ def auth_header(ts, key_id, secret_key, uri, payload) end describe "authorization" do - - it 'responds with a 401 error if connecting from unauthorized IP' do - # The whitelist is 0.0.0.0, and we reject all requests that don't have - # 0.0.0.0 as the second-to-last IP in X-Fowarded-For, as this isn't - # spoofable in PaaS - @request.headers['X-Forwarded-For'] = '1.2.3.4' - get :enquiries, params: { format: :json } - expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) - - @request.headers['X-Forwarded-For'] = '0.0.0.0' - get :enquiries, params: { format: :json } - expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) - - @request.headers['X-Forwarded-For'] = '1.2.3.4, 0.0.0.0' - get :enquiries, params: { format: :json } - expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) - - @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4, 123.123.123' - get :enquiries, params: { format: :json } - expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) - - @request.headers['X-Forwarded-For'] = '1.2.3.4, 123.123.123, 0.0.0.0' - get :enquiries, params: { format: :json } - expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) - end - it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' get :enquiries, params: { format: :json } From a35e2918267bc0982fab15956c978ea3865d5e93 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 22 Jan 2025 12:49:21 +0000 Subject: [PATCH 2/6] Check env before checking IPs --- app/controllers/api/activity_stream_controller.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/controllers/api/activity_stream_controller.rb b/app/controllers/api/activity_stream_controller.rb index 80f455c6c..b065bfe30 100644 --- a/app/controllers/api/activity_stream_controller.rb +++ b/app/controllers/api/activity_stream_controller.rb @@ -316,6 +316,14 @@ def to_search_after(object, method) end def authenticate(request) + unless ENV['COPILOT_ENVIRONMENT_NAME'] # DBT Platform + remote_ips = request.headers['X-Forwarded-For'].gsub(/\s+/, '').split(',') + return [false, 'Connecting from unauthorized IP'] unless remote_ips.length >= 2 + + authorized_ip_addresses = Figaro.env.ACTIVITY_STREAM_IP_WHITELIST.split(',') + return [false, 'Connecting from unauthorized IP'] unless authorized_ip_addresses.include?(remote_ips[-2]) + end + return [false, 'Authorization header is missing'] unless request.headers.key?('Authorization') parsed_header_array = request.headers['Authorization'].scan(/([a-z]+)="([^"]+)"/) From cd1f0c0882a970ea107e680f5135aee97fddc784 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 22 Jan 2025 13:32:08 +0000 Subject: [PATCH 3/6] Add back IP check tests --- spec/controllers/api/activity_stream_spec.rb | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/controllers/api/activity_stream_spec.rb b/spec/controllers/api/activity_stream_spec.rb index c1469ed03..bcdc18ce2 100644 --- a/spec/controllers/api/activity_stream_spec.rb +++ b/spec/controllers/api/activity_stream_spec.rb @@ -36,6 +36,28 @@ def auth_header(ts, key_id, secret_key, uri, payload) end describe "authorization" do + + it 'responds with a 401 error if connecting from unauthorized IP' do + # The whitelist is 0.0.0.0, and we reject all requests that don't have + # 0.0.0.0 as the second-to-last IP in X-Fowarded-For, as this isn't + # spoofable in PaaS + @request.headers['X-Forwarded-For'] = '1.2.3.4' + get :enquiries, params: { format: :json } + expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) + @request.headers['X-Forwarded-For'] = '0.0.0.0' + get :enquiries, params: { format: :json } + expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) + @request.headers['X-Forwarded-For'] = '1.2.3.4, 0.0.0.0' + get :enquiries, params: { format: :json } + expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) + @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4, 123.123.123' + get :enquiries, params: { format: :json } + expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) + @request.headers['X-Forwarded-For'] = '1.2.3.4, 123.123.123, 0.0.0.0' + get :enquiries, params: { format: :json } + expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) + end + it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' get :enquiries, params: { format: :json } From 9ef59e689c7f58d06c3eb453cea1e4c01d72a5f1 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 22 Jan 2025 16:46:43 +0000 Subject: [PATCH 4/6] Run rubocop -A --- spec/controllers/api/activity_stream_spec.rb | 231 +++++++++---------- 1 file changed, 111 insertions(+), 120 deletions(-) diff --git a/spec/controllers/api/activity_stream_spec.rb b/spec/controllers/api/activity_stream_spec.rb index bcdc18ce2..bba69bad5 100644 --- a/spec/controllers/api/activity_stream_spec.rb +++ b/spec/controllers/api/activity_stream_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'hawk' require 'json' require 'rails_helper' @@ -7,9 +9,9 @@ def auth_header(ts, key_id, secret_key, uri, payload) credentials = { id: key_id, key: secret_key, - algorithm: 'sha256', + algorithm: 'sha256' } - return Hawk::Client.build_authorization_header( + Hawk::Client.build_authorization_header( credentials: credentials, ts: ts, method: 'GET', @@ -17,7 +19,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) host: 'test.host', port: '443', content_type: '', - payload: payload, + payload: payload ) end @@ -27,7 +29,6 @@ def auth_header(ts, key_id, secret_key, uri, payload) end describe 'GET #enquiries' do - it 'if activity_stream is not enabled responds with a 403 error' do allow(Figaro.env).to receive('ACTIVITY_STREAM_ENABLED').and_return(nil) get :enquiries, params: { format: :json } @@ -35,8 +36,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) expect(response.body).to eq(%({"message":"Activity Stream is disabled"})) end - describe "authorization" do - + describe 'authorization' do it 'responds with a 401 error if connecting from unauthorized IP' do # The whitelist is 0.0.0.0, and we reject all requests that don't have # 0.0.0.0 as the second-to-last IP in X-Fowarded-For, as this isn't @@ -57,7 +57,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.body).to eq(%({"message":"Connecting from unauthorized IP"})) end - + it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' get :enquiries, params: { format: :json } @@ -67,12 +67,12 @@ def auth_header(ts, key_id, secret_key, uri, payload) it 'responds with a 401 if Authorization header in invalid format' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' - @request.headers['Authorization'] = 'Hawk' # Should have a space after + @request.headers['Authorization'] = 'Hawk' # Should have a space after get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk ' # Should not have two spaces after + @request.headers['Authorization'] = 'Hawk ' # Should not have two spaces after get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) @@ -92,27 +92,27 @@ def auth_header(ts, key_id, secret_key, uri, payload) expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk b="a" c="d"' # Should have commas + @request.headers['Authorization'] = 'Hawk b="a" c="d"' # Should have commas get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk, b="a", c="d"' # Should not have comma after Hawk + @request.headers['Authorization'] = 'Hawk, b="a", c="d"' # Should not have comma after Hawk get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk b="a",c="d"' # Should have space after comma + @request.headers['Authorization'] = 'Hawk b="a",c="d"' # Should have space after comma get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk b="a", c="d" ' # Should not have trailing space + @request.headers['Authorization'] = 'Hawk b="a", c="d" ' # Should not have trailing space get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) - @request.headers['Authorization'] = 'Hawk B="a"' # Keys must be lower case + @request.headers['Authorization'] = 'Hawk B="a"' # Keys must be lower case get :enquiries, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Invalid header"})) @@ -122,7 +122,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ).sub('Hawk ', 'AWS ') get :enquiries, params: { format: :json } @@ -134,8 +134,8 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', - ).sub('Hawk ', ' Hawk ') # Should not have leading space + '' + ).sub('Hawk ', ' Hawk ') # Should not have leading space get :enquiries, params: { format: :json } expect(response.status).to eq(401) @@ -146,7 +146,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ).sub('Hawk ', '') get :enquiries, params: { format: :json } @@ -158,7 +158,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ).sub('Hawk ', ', ') get :enquiries, params: { format: :json } @@ -170,7 +170,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ).sub('Hawk ', '", ') get :enquiries, params: { format: :json } @@ -185,7 +185,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } @@ -199,7 +199,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing ts") + expect(response.body).to include('Missing ts') end it 'responds with a 401 if Authorization header has non integer ts' do @@ -208,7 +208,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Invalid ts") + expect(response.body).to include('Invalid ts') end it 'responds with a 401 if Authorization header has empty ts' do @@ -217,7 +217,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing ts") + expect(response.body).to include('Missing ts') end it 'responds with a 401 if Authorization header misses mac' do @@ -226,7 +226,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing mac") + expect(response.body).to include('Missing mac') end it 'responds with a 401 if Authorization header has empty mac' do @@ -235,7 +235,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing mac") + expect(response.body).to include('Missing mac') end it 'responds with a 401 if Authorization header misses hash' do @@ -244,7 +244,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing hash") + expect(response.body).to include('Missing hash') end it 'responds with a 401 if Authorization header has empty hash' do @@ -253,7 +253,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing hash") + expect(response.body).to include('Missing hash') end it 'responds with a 401 if Authorization header misses nonce' do @@ -262,7 +262,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing hash") + expect(response.body).to include('Missing hash') end it 'responds with a 401 if Authorization header has empty nonce' do @@ -271,7 +271,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing hash") + expect(response.body).to include('Missing hash') end it 'responds with a 401 if Authorization header misses id' do @@ -280,7 +280,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing id") + expect(response.body).to include('Missing id') end it 'responds with a 401 if Authorization header has empty id' do @@ -289,17 +289,17 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Missing id") + expect(response.body).to include('Missing id') end it 'responds with a 401 if Authorization header uses incorrect key ID' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, - Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID + 'something-incorrect', + "#{Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID}something-incorrect", Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } @@ -312,14 +312,14 @@ def auth_header(ts, key_id, secret_key, uri, payload) @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, - Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY + 'something-incorrect', + "#{Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY}something-incorrect", activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Invalid mac") + expect(response.body).to include('Invalid mac') end it 'responds with a 401 if Authorization header uses incorrect payload' do @@ -329,12 +329,12 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - 'something-incorrect', + 'something-incorrect' ) get :enquiries, params: { format: :json } expect(response.status).to eq(401) - expect(response.body).to include("Invalid hash") + expect(response.body).to include('Invalid hash') end it 'responds with a 401 if header is reused' do @@ -344,7 +344,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } @@ -368,11 +368,11 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) begin get :enquiries, params: { format: :json } - rescue Redis::CannotConnectError => ex + rescue Redis::CannotConnectError => e end expect(ex.backtrace.to_s).to include('/redis/') end @@ -384,18 +384,16 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } expect(JSON.parse(response.body)['orderedItems']).to eq([]) expect(response.headers['Content-Type']).to eq('application/json; charset=utf-8') end - end - describe "content" do - + describe 'content' do it 'does not have any entry elements if an enquiry made without a company number' do create(:enquiry, company_house_number: nil) @@ -405,7 +403,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } @@ -421,7 +419,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } @@ -443,7 +441,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } feed_hash = JSON.parse(response.body) @@ -451,7 +449,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) items = feed_hash['orderedItems'] expect(items.length).to eq(1) - item = items[0] + item = items[0] expect(item['id']).to eq("dit:exportOpportunities:Enquiry:#{enquiry.id}:Create") expect(item['type']).to eq('Create') expect(item['object']['published']).to eq('2008-09-01T12:01:02+00:00') @@ -482,7 +480,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } feed_hash = JSON.parse(response.body) @@ -493,7 +491,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) elastic_search_bulk_1 = items[0] expect(elastic_search_bulk_1['object']['published']).to eq('2008-09-01T12:01:02+00:00') - elastic_search_bulk_2 = items[1] + elastic_search_bulk_2 = items[1] expect(elastic_search_bulk_2['object']['published']).to eq('2008-09-01T12:01:03+00:00') end @@ -514,7 +512,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } feed_hash = JSON.parse(response.body) @@ -524,7 +522,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) elastic_search_bulk_1 = items[0] expect(elastic_search_bulk_1['actor'][0]['dit:companiesHouseNumber']).to eq('124') - elastic_search_bulk_2 = items[1] + elastic_search_bulk_2 = items[1] expect(elastic_search_bulk_2['actor'][0]['dit:companiesHouseNumber']).to eq('123') end @@ -533,10 +531,10 @@ def auth_header(ts, key_id, secret_key, uri, payload) country_2 = create(:country) # Creating records takes quite a while. Stub for a quicker test - stub_const("MAX_PER_PAGE", 20) - Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344590)) do - for i in 1..21 do - enquiry = create(:enquiry, company_house_number: i.to_s, id:(2923 + i)) + stub_const('MAX_PER_PAGE', 20) + Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344_590)) do + (1..21).each do |i| + enquiry = create(:enquiry, company_house_number: i.to_s, id: (2923 + i)) enquiry.opportunity.countries = [country_1, country_2] end end @@ -547,7 +545,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, - '', + '' ) get :enquiries, params: { format: :json } feed_hash_1 = JSON.parse(response.body) @@ -562,7 +560,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_enquiries_path}?search_after=1220270462.344590_2943", - '', + '' ) get :enquiries, params: { format: :json, search_after: '1220270462.344590_2943' } feed_hash_2 = JSON.parse(response.body) @@ -574,7 +572,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_enquiries_path}?search_after=1220270462.344590_2944", - '', + '' ) get :enquiries, params: { format: :json, search_after: '1220270462.344590_2944' } feed_hash_3 = JSON.parse(response.body) @@ -585,9 +583,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) end describe 'GET #opportunities' do - - describe "content" do - + describe 'content' do before do Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2)) end @@ -596,31 +592,31 @@ def auth_header(ts, key_id, secret_key, uri, payload) Timecop.return end - def create_opportunity(state=:published, options={}) + def create_opportunity(state = :published, options = {}) options = { - title: options[:title] || "2x4 Wood", - slug: options[:slug] ||"2x4-wood", - buyer_name: options[:buyer_name] || "Xi Weng Manufacturing", - buyer_address: options[:buyer_address] || - "2002 Jiabin Rd, RenMin NanLu, Luohu Qu, Shenzhen Shi, Guangdong Sheng, China, 518011", + title: options[:title] || '2x4 Wood', + slug: options[:slug] || '2x4-wood', + buyer_name: options[:buyer_name] || 'Xi Weng Manufacturing', + buyer_address: options[:buyer_address] || + '2002 Jiabin Rd, RenMin NanLu, Luohu Qu, Shenzhen Shi, Guangdong Sheng, China, 518011', first_published_at: options[:first_published_at] || Time.utc(2001, 9, 3), - response_due_on: options[:response_due_on] || Time.utc(2008, 12, 1), - teaser: options[:teaser] || "Looking for 50kg of 2x4 Oak Wood Blocks", - description: options[:description] || - "We are proud to announce the tender for our annual wood block requirement. - We are looking for 50kg of 2x4 Oak Wood Blocks" + response_due_on: options[:response_due_on] || Time.utc(2008, 12, 1), + teaser: options[:teaser] || 'Looking for 50kg of 2x4 Oak Wood Blocks', + description: options[:description] || + "We are proud to announce the tender for our annual wood block requirement. + We are looking for 50kg of 2x4 Oak Wood Blocks" } create(:opportunity, state, options) end - def get_feed(path, params={}) + def get_feed(path, params = {}) @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, path, - '', + '' ) get :opportunities, params: { format: :json }.merge(params) @@ -631,7 +627,7 @@ def get_feed(path, params={}) opportunity = create_opportunity(:published) items = get_feed(activity_stream_opportunities_path) - uri = URI.parse(ENV["DOMAIN"]) + uri = URI.parse(ENV['DOMAIN']) domain = "#{uri.scheme}://#{uri.host}" expect(items.length).to eq(1) @@ -646,7 +642,7 @@ def get_feed(path, params={}) expect(item['object']['published']).to eq('2001-09-03T00:00:00+00:00') expect(item['object']['endTime']).to eq('2008-12-01T00:00:00+00:00') expect(item['object']['summary']).to eq('Looking for 50kg of 2x4 Oak Wood Blocks') - expect(item['object']['content']).to eq('We are proud to announce the tender for our annual wood block requirement. + expect(item['object']['content']).to eq('We are proud to announce the tender for our annual wood block requirement. We are looking for 50kg of 2x4 Oak Wood Blocks') expect(item['object']['attributedTo'][0]['name']).to eq('post') end @@ -660,14 +656,14 @@ def get_feed(path, params={}) it 'returns opportunities in date order' do # Create two an opportunities - for i in 0..1 do + (0..1).each do |i| op = create_opportunity(:published, { - title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}" - }) + title: "2x4 Wood #{i}", + slug: "2x4-wood-#{i}" + }) op.update_column(:updated_at, Time.now + i.days) end - + items = get_feed(activity_stream_opportunities_path) expect(items.length).to eq(2) @@ -678,11 +674,11 @@ def get_feed(path, params={}) it 'in ID order if two opportunities are made at the same time' do Opportunity.destroy_all - for i in 0..1 do + (0..1).each do |i| op = create_opportunity(:published, { - title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}", - }) + title: "2x4 Wood #{i}", + slug: "2x4-wood-#{i}" + }) op.update_column(:updated_at, Time.now) end @@ -695,21 +691,20 @@ def get_feed(path, params={}) it 'is paginated with a link element if there are MAX_PER_PAGE opportunities' do Opportunity.destroy_all - ServiceProvider.create(name: "British Embassy") + ServiceProvider.create(name: 'British Embassy') - stub_const("MAX_PER_PAGE", 20) - Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344590)) do - for i in 1..21 do + stub_const('MAX_PER_PAGE', 20) + Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344_590)) do + (1..21).each do |i| op = create_opportunity(:published, { - title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}", - service_provider: ServiceProvider.find(ServiceProvider.ids.shuffle.first) - }) + title: "2x4 Wood #{i}", + slug: "2x4-wood-#{i}", + service_provider: ServiceProvider.find(ServiceProvider.ids.sample) + }) op.update_column(:updated_at, Time.now + i.days) end end - # Fetch data for search_after() to help generate 'next' URL twenty = Opportunity.order(:updated_at)[19] id_20 = twenty.id @@ -724,7 +719,7 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_opportunities_path, - '', + '' ) get :opportunities, params: { format: :json } feed_hash_1 = JSON.parse(response.body) @@ -739,11 +734,11 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_opportunities_path}?search_after=#{timestamp_20}_#{id_20}", - '', + '' ) get :opportunities, params: { format: :json, search_after: "#{timestamp_20}_#{id_20}" } feed_hash_2 = JSON.parse(response.body) - + expect(feed_hash_2.key?('next')).to eq(true) expect(feed_hash_2['orderedItems'][0]['id']).to eq("dit:exportOpportunities:Opportunity:#{id_21}:Create") @@ -752,29 +747,26 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_opportunities_path}?search_after=#{timestamp_21}_#{id_21}", - '', + '' ) get :opportunities, params: { format: :json, search_after: "#{timestamp_21}_#{id_21}" } feed_hash_3 = JSON.parse(response.body) expect(feed_hash_3.key?('next')).to eq(false) expect(feed_hash_3['orderedItems']).to eq([]) end - end - describe "authorization" do - + describe 'authorization' do it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' get :opportunities, params: { format: :json } expect(response.status).to eq(401) expect(response.body).to eq(%({"message":"Authorization header is missing"})) end - end end - describe 'GET #csat_feedback' do + describe 'GET #csat_feedback' do describe 'content' do before do Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2)) @@ -784,14 +776,14 @@ def get_feed(path, params={}) Timecop.return end - def get_feed(path, params={}) + def get_feed(path, params = {}) @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, path, - '', + '' ) get :csat_feedback, params: { format: :json }.merge(params) @@ -803,12 +795,12 @@ def get_feed(path, params={}) items = get_feed(activity_stream_csat_feedback_path) - uri = URI.parse(ENV["DOMAIN"]) + uri = URI.parse(ENV['DOMAIN']) domain = "#{uri.scheme}://#{uri.host}" expect(items.length).to eq(1) item = items[0] - item_object = item["object"] + item_object = item['object'] expect(item['id']).to eq("dit:exportOpportunities:HCSATFeedbackData:#{feedback.id}:Update") expect(item['type']).to eq('Update') @@ -827,14 +819,14 @@ def get_feed(path, params={}) it 'is paginated with a link element if there are MAX_PER_PAGE csat feedback responses' do CustomerSatisfactionFeedback.destroy_all - stub_const("MAX_PER_PAGE", 20) - Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344590)) do - for i in 1..21 do + stub_const('MAX_PER_PAGE', 20) + Timecop.freeze(Time.utc(2008, 9, 1, 12, 1, 2, 344_590)) do + (1..21).each do |i| feedback = create(:csat_feedback) feedback.update_column(:created_at, Time.now + i.days) end end - + # Fetch data for search_after() to help generate 'next' URL twenty = CustomerSatisfactionFeedback.order(:created_at)[19] id_20 = twenty.id @@ -849,7 +841,7 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_csat_feedback_path, - '', + '' ) get :csat_feedback, params: { format: :json } feed_hash_1 = JSON.parse(response.body) @@ -864,11 +856,11 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_csat_feedback_path}?search_after=#{timestamp_20}_#{id_20}", - '', + '' ) get :csat_feedback, params: { format: :json, search_after: "#{timestamp_20}_#{id_20}" } feed_hash_2 = JSON.parse(response.body) - + expect(feed_hash_2.key?('next')).to eq(true) expect(feed_hash_2['orderedItems'][0]['id']).to eq("dit:exportOpportunities:HCSATFeedbackData:#{id_21}:Update") @@ -877,7 +869,7 @@ def get_feed(path, params={}) Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, "#{activity_stream_csat_feedback_path}?search_after=#{timestamp_21}_#{id_21}", - '', + '' ) get :csat_feedback, params: { format: :json, search_after: "#{timestamp_21}_#{id_21}" } feed_hash_3 = JSON.parse(response.body) @@ -885,9 +877,8 @@ def get_feed(path, params={}) expect(feed_hash_3['orderedItems']).to eq([]) end end - - describe "authorization" do + describe 'authorization' do it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' get :csat_feedback, params: { format: :json } From 54dd016ab9fc1ec7ac62c81544c9b7aad744d7a2 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 22 Jan 2025 17:10:08 +0000 Subject: [PATCH 5/6] Fix test and remove uneeded linting changes --- spec/controllers/api/activity_stream_spec.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/spec/controllers/api/activity_stream_spec.rb b/spec/controllers/api/activity_stream_spec.rb index bba69bad5..ab0a9768f 100644 --- a/spec/controllers/api/activity_stream_spec.rb +++ b/spec/controllers/api/activity_stream_spec.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'hawk' require 'json' require 'rails_helper' @@ -296,7 +294,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, - "#{Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID}something-incorrect", + Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID + 'something-incorrect', Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY, activity_stream_enquiries_path, '' @@ -312,7 +310,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) @request.headers['Authorization'] = auth_header( Time.now.getutc.to_i, Figaro.env.ACTIVITY_STREAM_ACCESS_KEY_ID, - "#{Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY}something-incorrect", + Figaro.env.ACTIVITY_STREAM_SECRET_ACCESS_KEY + 'something-incorrect', activity_stream_enquiries_path, '' ) @@ -374,7 +372,7 @@ def auth_header(ts, key_id, secret_key, uri, payload) get :enquiries, params: { format: :json } rescue Redis::CannotConnectError => e end - expect(ex.backtrace.to_s).to include('/redis/') + expect(e.backtrace.to_s).to include('/redis/') end it 'responds with no items if Authorization header is set and correct' do @@ -643,7 +641,7 @@ def get_feed(path, params = {}) expect(item['object']['endTime']).to eq('2008-12-01T00:00:00+00:00') expect(item['object']['summary']).to eq('Looking for 50kg of 2x4 Oak Wood Blocks') expect(item['object']['content']).to eq('We are proud to announce the tender for our annual wood block requirement. - We are looking for 50kg of 2x4 Oak Wood Blocks') + We are looking for 50kg of 2x4 Oak Wood Blocks') expect(item['object']['attributedTo'][0]['name']).to eq('post') end @@ -659,7 +657,7 @@ def get_feed(path, params = {}) (0..1).each do |i| op = create_opportunity(:published, { title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}" + slug: "2x4-wood-#{i}" }) op.update_column(:updated_at, Time.now + i.days) end @@ -677,7 +675,7 @@ def get_feed(path, params = {}) (0..1).each do |i| op = create_opportunity(:published, { title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}" + slug: "2x4-wood-#{i}" }) op.update_column(:updated_at, Time.now) end @@ -698,8 +696,8 @@ def get_feed(path, params = {}) (1..21).each do |i| op = create_opportunity(:published, { title: "2x4 Wood #{i}", - slug: "2x4-wood-#{i}", - service_provider: ServiceProvider.find(ServiceProvider.ids.sample) + slug: "2x4-wood-#{i}", + service_provider: ServiceProvider.find(ServiceProvider.ids.sample) }) op.update_column(:updated_at, Time.now + i.days) end @@ -878,6 +876,7 @@ def get_feed(path, params = {}) end end + describe 'authorization' do it 'responds with a 401 error if Authorization header is not set' do @request.headers['X-Forwarded-For'] = '0.0.0.0, 1.2.3.4' From 1cda9f4d05a3e74fa4375c8d8a70eca7bcc37944 Mon Sep 17 00:00:00 2001 From: aamircodes Date: Fri, 17 Jan 2025 15:09:02 +0000 Subject: [PATCH 6/6] fix: procfile web process for DBT --- .copilot/config.yml | 2 +- .copilot/image_build_run.sh | 2 ++ Procfile | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.copilot/config.yml b/.copilot/config.yml index c72dda0ec..c4ae3d5ce 100644 --- a/.copilot/config.yml +++ b/.copilot/config.yml @@ -1,4 +1,4 @@ repository: export-opportunities builder: name: paketobuildpacks/builder-jammy-full - version: 0.3.397 + version: 0.3.397 \ No newline at end of file diff --git a/.copilot/image_build_run.sh b/.copilot/image_build_run.sh index 4e2bfeec8..6eb6a0bf4 100755 --- a/.copilot/image_build_run.sh +++ b/.copilot/image_build_run.sh @@ -7,6 +7,8 @@ set -e rm ./config/application.yml +cp -r /workspace/db/ /workspace/db-copy/ + rm -rf /workspace/tmp rm -rf /workspace/db rm -rf /workspace/log diff --git a/Procfile b/Procfile index 329785eea..593dabf6e 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec rake cf:on_first_instance db:migrate && bundle exec puma -p ${PORT:-3000} -app: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails assets:precompile && bundle exec puma -p ${PORT:-3000} +app: cp -r /workspace/db-copy/* /workspace/db/ && bundle exec rake cf:on_first_instance db:migrate && bundle exec rails assets:precompile && bundle exec puma -p ${PORT:-3000} worker: bin/start-stunnel bundle exec sidekiq -C config/sidekiq.yml