castle-rb 8.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +317 -0
  3. data/LICENSE +21 -0
  4. data/README.md +290 -112
  5. data/lib/castle/api/filter.rb +3 -1
  6. data/lib/castle/api/list_items/archive.rb +23 -0
  7. data/lib/castle/api/list_items/count.rb +22 -0
  8. data/lib/castle/api/list_items/create.rb +22 -0
  9. data/lib/castle/api/list_items/create_batch.rb +22 -0
  10. data/lib/castle/api/list_items/get.rb +22 -0
  11. data/lib/castle/api/list_items/query.rb +22 -0
  12. data/lib/castle/api/list_items/unarchive.rb +22 -0
  13. data/lib/castle/api/list_items/update.rb +22 -0
  14. data/lib/castle/api/lists/create.rb +23 -0
  15. data/lib/castle/api/lists/delete.rb +22 -0
  16. data/lib/castle/api/lists/get.rb +22 -0
  17. data/lib/castle/api/lists/get_all.rb +22 -0
  18. data/lib/castle/api/lists/query.rb +22 -0
  19. data/lib/castle/api/lists/update.rb +22 -0
  20. data/lib/castle/api/log.rb +2 -1
  21. data/lib/castle/api/privacy/delete_data.rb +23 -0
  22. data/lib/castle/api/privacy/request_data.rb +23 -0
  23. data/lib/castle/api/risk.rb +2 -1
  24. data/lib/castle/client.rb +15 -52
  25. data/lib/castle/client_actions/list_items.rb +49 -0
  26. data/lib/castle/client_actions/lists.rb +39 -0
  27. data/lib/castle/client_actions/privacy.rb +20 -0
  28. data/lib/castle/commands/list_items/archive.rb +24 -0
  29. data/lib/castle/commands/list_items/count.rb +23 -0
  30. data/lib/castle/commands/list_items/create.rb +22 -0
  31. data/lib/castle/commands/list_items/create_batch.rb +22 -0
  32. data/lib/castle/commands/list_items/get.rb +23 -0
  33. data/lib/castle/commands/list_items/query.rb +24 -0
  34. data/lib/castle/commands/list_items/unarchive.rb +23 -0
  35. data/lib/castle/commands/list_items/update.rb +22 -0
  36. data/lib/castle/commands/lists/create.rb +21 -0
  37. data/lib/castle/commands/lists/delete.rb +20 -0
  38. data/lib/castle/commands/lists/get.rb +20 -0
  39. data/lib/castle/commands/lists/get_all.rb +17 -0
  40. data/lib/castle/commands/lists/query.rb +21 -0
  41. data/lib/castle/commands/lists/update.rb +22 -0
  42. data/lib/castle/commands/privacy/delete_data.rb +20 -0
  43. data/lib/castle/commands/privacy/request_data.rb +20 -0
  44. data/lib/castle/core/get_connection.rb +5 -1
  45. data/lib/castle/core/process_response.rb +1 -0
  46. data/lib/castle/core/process_webhook.rb +1 -1
  47. data/lib/castle/core/send_request.rb +2 -1
  48. data/lib/castle/errors.rb +1 -5
  49. data/lib/castle/headers/filter.rb +1 -1
  50. data/lib/castle/version.rb +1 -1
  51. data/lib/castle.rb +36 -17
  52. metadata +62 -162
  53. data/lib/castle/api/approve_device.rb +0 -20
  54. data/lib/castle/api/authenticate.rb +0 -28
  55. data/lib/castle/api/end_impersonation.rb +0 -22
  56. data/lib/castle/api/get_device.rb +0 -20
  57. data/lib/castle/api/get_devices_for_user.rb +0 -20
  58. data/lib/castle/api/report_device.rb +0 -20
  59. data/lib/castle/api/start_impersonation.rb +0 -22
  60. data/lib/castle/api/track.rb +0 -19
  61. data/lib/castle/commands/approve_device.rb +0 -17
  62. data/lib/castle/commands/authenticate.rb +0 -23
  63. data/lib/castle/commands/end_impersonation.rb +0 -25
  64. data/lib/castle/commands/get_device.rb +0 -17
  65. data/lib/castle/commands/get_devices_for_user.rb +0 -17
  66. data/lib/castle/commands/report_device.rb +0 -17
  67. data/lib/castle/commands/start_impersonation.rb +0 -25
  68. data/lib/castle/commands/track.rb +0 -22
  69. data/lib/castle/support/hanami.rb +0 -15
  70. data/lib/castle/support/padrino.rb +0 -19
  71. data/spec/integration/rails/rails_spec.rb +0 -95
  72. data/spec/integration/rails/support/all.rb +0 -6
  73. data/spec/integration/rails/support/application.rb +0 -17
  74. data/spec/integration/rails/support/home_controller.rb +0 -39
  75. data/spec/lib/castle/api/approve_device_spec.rb +0 -17
  76. data/spec/lib/castle/api/authenticate_spec.rb +0 -133
  77. data/spec/lib/castle/api/end_impersonation_spec.rb +0 -59
  78. data/spec/lib/castle/api/filter_spec.rb +0 -5
  79. data/spec/lib/castle/api/get_device_spec.rb +0 -17
  80. data/spec/lib/castle/api/get_devices_for_user_spec.rb +0 -17
  81. data/spec/lib/castle/api/log_spec.rb +0 -5
  82. data/spec/lib/castle/api/report_device_spec.rb +0 -17
  83. data/spec/lib/castle/api/risk_spec.rb +0 -5
  84. data/spec/lib/castle/api/start_impersonation_spec.rb +0 -59
  85. data/spec/lib/castle/api/track_spec.rb +0 -65
  86. data/spec/lib/castle/api_spec.rb +0 -36
  87. data/spec/lib/castle/client_id/extract_spec.rb +0 -47
  88. data/spec/lib/castle/client_spec.rb +0 -337
  89. data/spec/lib/castle/command_spec.rb +0 -9
  90. data/spec/lib/castle/commands/approve_device_spec.rb +0 -24
  91. data/spec/lib/castle/commands/authenticate_spec.rb +0 -86
  92. data/spec/lib/castle/commands/end_impersonation_spec.rb +0 -72
  93. data/spec/lib/castle/commands/filter_spec.rb +0 -72
  94. data/spec/lib/castle/commands/get_device_spec.rb +0 -24
  95. data/spec/lib/castle/commands/get_devices_for_user_spec.rb +0 -24
  96. data/spec/lib/castle/commands/log_spec.rb +0 -73
  97. data/spec/lib/castle/commands/report_device_spec.rb +0 -24
  98. data/spec/lib/castle/commands/risk_spec.rb +0 -73
  99. data/spec/lib/castle/commands/start_impersonation_spec.rb +0 -72
  100. data/spec/lib/castle/commands/track_spec.rb +0 -89
  101. data/spec/lib/castle/configuration_spec.rb +0 -14
  102. data/spec/lib/castle/context/get_default_spec.rb +0 -41
  103. data/spec/lib/castle/context/merge_spec.rb +0 -23
  104. data/spec/lib/castle/context/prepare_spec.rb +0 -42
  105. data/spec/lib/castle/context/sanitize_spec.rb +0 -27
  106. data/spec/lib/castle/core/get_connection_spec.rb +0 -43
  107. data/spec/lib/castle/core/process_response_spec.rb +0 -103
  108. data/spec/lib/castle/core/process_webhook_spec.rb +0 -52
  109. data/spec/lib/castle/core/send_request_spec.rb +0 -97
  110. data/spec/lib/castle/failover/strategy_spec.rb +0 -12
  111. data/spec/lib/castle/headers/extract_spec.rb +0 -103
  112. data/spec/lib/castle/headers/filter_spec.rb +0 -42
  113. data/spec/lib/castle/headers/format_spec.rb +0 -25
  114. data/spec/lib/castle/ips/extract_spec.rb +0 -91
  115. data/spec/lib/castle/logger_spec.rb +0 -39
  116. data/spec/lib/castle/payload/prepare_spec.rb +0 -52
  117. data/spec/lib/castle/secure_mode_spec.rb +0 -7
  118. data/spec/lib/castle/session_spec.rb +0 -61
  119. data/spec/lib/castle/singleton_configuration_spec.rb +0 -14
  120. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +0 -69
  121. data/spec/lib/castle/utils/clone_spec.rb +0 -19
  122. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +0 -50
  123. data/spec/lib/castle/utils/get_timestamp_spec.rb +0 -16
  124. data/spec/lib/castle/utils/merge_spec.rb +0 -13
  125. data/spec/lib/castle/validators/not_supported_spec.rb +0 -19
  126. data/spec/lib/castle/validators/present_spec.rb +0 -25
  127. data/spec/lib/castle/verdict_spec.rb +0 -9
  128. data/spec/lib/castle/version_spec.rb +0 -5
  129. data/spec/lib/castle/webhooks/verify_spec.rb +0 -59
  130. data/spec/lib/castle_spec.rb +0 -58
  131. data/spec/spec_helper.rb +0 -24
  132. data/spec/support/shared_examples/action_request.rb +0 -167
  133. data/spec/support/shared_examples/configuration.rb +0 -99
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Context::Merge do
4
- let(:first) { { test: { test1: { c: '4' } } } }
5
-
6
- describe '#call' do
7
- subject { described_class.call(first, second) }
8
-
9
- let(:result) { { test: { test1: { c: '4', d: '5' } } } }
10
-
11
- context 'with symbol keys' do
12
- let(:second) { { test: { test1: { d: '5' } } } }
13
-
14
- it { is_expected.to eq(result) }
15
- end
16
-
17
- context 'with string keys' do
18
- let(:second) { { 'test' => { 'test1' => { 'd' => '5' } } } }
19
-
20
- it { is_expected.to eq(result) }
21
- end
22
- end
23
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Context::Prepare do
4
- let(:ip) { '1.2.3.4' }
5
- let(:cookie_id) { 'abcd' }
6
- let(:ua) { 'Chrome' }
7
- let(:env) do
8
- Rack::MockRequest.env_for(
9
- '/',
10
- 'HTTP_USER_AGENT' => ua,
11
- 'HTTP_X_FORWARDED_FOR' => ip,
12
- 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh",
13
- 'HTTP_CONTENT_LENGTH' => '0'
14
- )
15
- end
16
- let(:request) { Rack::Request.new(env) }
17
- let(:context) do
18
- {
19
- client_id: 'abcd',
20
- active: true,
21
- user_agent: ua,
22
- headers: headers,
23
- ip: ip,
24
- library: {
25
- name: 'castle-rb',
26
- version: '6.0.0'
27
- }
28
- }
29
- end
30
-
31
- let(:headers) { { 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, Cookie: true } }
32
-
33
- before { stub_const('Castle::VERSION', '6.0.0') }
34
-
35
- describe '#call' do
36
- subject(:generated) { described_class.call(request) }
37
-
38
- context 'when active true' do
39
- it { is_expected.to eql(context) }
40
- end
41
- end
42
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Context::Sanitize do
4
- let(:paylod) { { test: 'test' } }
5
-
6
- describe '#call' do
7
- subject { described_class.call(context) }
8
-
9
- context 'when active true' do
10
- let(:context) { paylod.merge(active: true) }
11
-
12
- it { is_expected.to eql(context) }
13
- end
14
-
15
- context 'when active false' do
16
- let(:context) { paylod.merge(active: false) }
17
-
18
- it { is_expected.to eql(context) }
19
- end
20
-
21
- context 'when active string' do
22
- let(:context) { paylod.merge(active: 'uknown') }
23
-
24
- it { is_expected.to eql(paylod) }
25
- end
26
- end
27
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Core::GetConnection do
4
- describe '.call' do
5
- subject(:class_call) { described_class.call }
6
-
7
- context 'when ssl false' do
8
- let(:localhost) { 'localhost' }
9
- let(:port) { 3002 }
10
- let(:api_url) { '/test' }
11
-
12
- before do
13
- Castle.config.base_url = 'http://localhost:3002'
14
-
15
- allow(Net::HTTP).to receive(:new).with(localhost, port).and_call_original
16
- end
17
-
18
- it do
19
- class_call
20
-
21
- expect(Net::HTTP).to have_received(:new).with(localhost, port)
22
- end
23
-
24
- it { expect(class_call).to be_an_instance_of(Net::HTTP) }
25
- end
26
-
27
- context 'when ssl true' do
28
- let(:localhost) { 'localhost' }
29
- let(:port) { 443 }
30
-
31
- before { Castle.config.base_url = 'https://localhost' }
32
-
33
- context 'with block' do
34
- let(:api_url) { '/test' }
35
- let(:request) { Net::HTTP::Get.new(api_url) }
36
-
37
- before { allow(Net::HTTP).to receive(:new).with(localhost, port).and_call_original }
38
-
39
- it { expect(class_call).to be_an_instance_of(Net::HTTP) }
40
- end
41
- end
42
- end
43
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Core::ProcessResponse do
4
- describe '#call' do
5
- subject(:call) { described_class.call(response) }
6
-
7
- context 'when success' do
8
- let(:response) { OpenStruct.new(body: '{"user":1}', code: 200) }
9
-
10
- it { expect(call).to eql(user: 1) }
11
- end
12
-
13
- describe 'authenticate' do
14
- context 'when allow without any additional props' do
15
- let(:response) { OpenStruct.new(body: '{"action":"allow","user_id":"12345"}', code: 200) }
16
-
17
- it { expect(call).to eql({ action: 'allow', user_id: '12345' }) }
18
- end
19
-
20
- context 'when allow with additional props' do
21
- let(:response) { OpenStruct.new(body: '{"action":"allow","user_id":"12345","internal":{}}', code: 200) }
22
-
23
- it { expect(call).to eql({ action: 'allow', user_id: '12345', internal: {} }) }
24
- end
25
-
26
- context 'when deny without risk policy' do
27
- let(:response) { OpenStruct.new(body: '{"action":"deny","user_id":"1","device_token":"abc"}', code: 200) }
28
-
29
- it { expect(call).to eql({ action: 'deny', user_id: '1', device_token: 'abc' }) }
30
- end
31
-
32
- context 'when deny with risk policy' do
33
- let(:body) do
34
- '{"action":"deny","user_id":"1","device_token":"abc",
35
- "risk_policy":{"id":"123","revision_id":"abc","name":"def","type":"bot"}}'
36
- end
37
- let(:response) { OpenStruct.new({ body: body, code: 200 }) }
38
-
39
- let(:result) do
40
- {
41
- action: 'deny',
42
- user_id: '1',
43
- device_token: 'abc',
44
- risk_policy: {
45
- id: '123',
46
- revision_id: 'abc',
47
- name: 'def',
48
- type: 'bot'
49
- }
50
- }
51
- end
52
-
53
- it { expect(call).to eql(result) }
54
- end
55
- end
56
-
57
- context 'when response empty' do
58
- let(:response) { OpenStruct.new(body: '', code: 200) }
59
-
60
- it { expect(call).to eql({}) }
61
- end
62
-
63
- context 'when response nil' do
64
- let(:response) { OpenStruct.new(code: 200) }
65
-
66
- it { expect(call).to eql({}) }
67
- end
68
-
69
- context 'when json is malformed' do
70
- let(:response) { OpenStruct.new(body: '{a', code: 200) }
71
-
72
- it { expect { call }.to raise_error(Castle::ApiError) }
73
- end
74
- end
75
-
76
- describe '#verify!' do
77
- subject(:verify!) { described_class.verify!(response) }
78
-
79
- context 'without error when response is 2xx' do
80
- let(:response) { OpenStruct.new(code: 200) }
81
-
82
- it { expect { verify! }.not_to raise_error }
83
- end
84
-
85
- shared_examples 'response_failed' do |code, error|
86
- let(:response) { OpenStruct.new(code: code) }
87
-
88
- it "fail when response is #{code}" do
89
- expect { verify! }.to raise_error(error)
90
- end
91
- end
92
-
93
- it_behaves_like 'response_failed', '400', Castle::BadRequestError
94
- it_behaves_like 'response_failed', '401', Castle::UnauthorizedError
95
- it_behaves_like 'response_failed', '403', Castle::ForbiddenError
96
- it_behaves_like 'response_failed', '404', Castle::NotFoundError
97
- it_behaves_like 'response_failed', '419', Castle::UserUnauthorizedError
98
- it_behaves_like 'response_failed', '422', Castle::InvalidParametersError
99
- it_behaves_like 'response_failed', '429', Castle::RateLimitError
100
- it_behaves_like 'response_failed', '499', Castle::ApiError
101
- it_behaves_like 'response_failed', '500', Castle::InternalServerError
102
- end
103
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Core::ProcessWebhook do
4
- describe '#call' do
5
- subject(:call) { described_class.call(webhook) }
6
-
7
- let(:webhook_body) do
8
- {
9
- api_version: 'v1',
10
- app_id: '12345',
11
- type: '$incident.confirmed',
12
- created_at: '2020-12-18T12:55:21.779Z',
13
- data: {
14
- id: 'test',
15
- device_token: 'token',
16
- user_id: '',
17
- trigger: '$login.succeeded',
18
- context: {
19
- },
20
- location: {
21
- },
22
- user_agent: {
23
- }
24
- },
25
- user_traits: {
26
- },
27
- properties: {
28
- },
29
- policy: {
30
- }
31
- }.to_json
32
- end
33
-
34
- let(:webhook) { OpenStruct.new(body: StringIO.new(webhook_body)) }
35
-
36
- context 'when success' do
37
- it { expect(call).to eql(webhook_body) }
38
- end
39
-
40
- context 'when webhook empty' do
41
- let(:webhook) { OpenStruct.new(body: StringIO.new('')) }
42
-
43
- it { expect { call }.to raise_error(Castle::ApiError, 'Invalid webhook from Castle API') }
44
- end
45
-
46
- context 'when webhook nil' do
47
- let(:webhook) { OpenStruct.new(body: StringIO.new) }
48
-
49
- it { expect { call }.to raise_error(Castle::ApiError, 'Invalid webhook from Castle API') }
50
- end
51
- end
52
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Core::SendRequest do
4
- let(:config) { Castle.config }
5
-
6
- describe '#call' do
7
- let(:command) { Castle::Commands::Track.build(event: '$login.succeeded') }
8
- let(:headers) { {} }
9
- let(:request_build) { {} }
10
- let(:expected_headers) { { 'Content-Type' => 'application/json' } }
11
- let(:http) { instance_double(Net::HTTP) }
12
-
13
- context 'without http arg provided' do
14
- subject(:call) { described_class.call(command, headers, nil, config) }
15
-
16
- let(:http) { instance_double(Net::HTTP) }
17
- let(:command) { Castle::Commands::Track.build(event: '$login.succeeded') }
18
- let(:headers) { {} }
19
- let(:request_build) { {} }
20
- let(:expected_headers) { { 'Content-Type' => 'application/json' } }
21
-
22
- before do
23
- allow(Castle::Core::GetConnection).to receive(:call).and_return(http)
24
- allow(http).to receive(:request)
25
- allow(described_class).to receive(:build).and_return(request_build)
26
- call
27
- end
28
-
29
- it { expect(described_class).to have_received(:build).with(command, expected_headers, config) }
30
-
31
- it { expect(http).to have_received(:request).with(request_build) }
32
- end
33
-
34
- context 'with http arg provided' do
35
- subject(:call) { described_class.call(command, headers, http, config) }
36
-
37
- before do
38
- allow(Castle::Core::GetConnection).to receive(:call)
39
- allow(http).to receive(:request)
40
- allow(described_class).to receive(:build).and_return(request_build)
41
- call
42
- end
43
-
44
- it { expect(Castle::Core::GetConnection).not_to have_received(:call) }
45
-
46
- it { expect(described_class).to have_received(:build).with(command, expected_headers, config) }
47
-
48
- it { expect(http).to have_received(:request).with(request_build) }
49
- end
50
- end
51
-
52
- describe '#build' do
53
- subject(:build) { described_class.build(command, headers, config) }
54
-
55
- let(:headers) { { 'SAMPLE-HEADER' => '1' } }
56
- let(:api_secret) { 'secret' }
57
-
58
- context 'when post' do
59
- let(:time) { Time.now.utc.iso8601(3) }
60
- let(:command) { Castle::Commands::Track.build(event: '$login.succeeded', name: "\xC4") }
61
- let(:expected_body) { { event: '$login.succeeded', name: '�', context: {}, sent_at: time } }
62
-
63
- before { allow(Castle::Utils::GetTimestamp).to receive(:call).and_return(time) }
64
-
65
- it { expect(build.body).to eql(expected_body.to_json) }
66
- it { expect(build.method).to eql('POST') }
67
- it { expect(build.path).to eql("/v1/#{command.path}") }
68
- it { expect(build.to_hash).to have_key('authorization') }
69
- it { expect(build.to_hash).to have_key('sample-header') }
70
- it { expect(build.to_hash['sample-header']).to eql(['1']) }
71
- end
72
-
73
- context 'when get' do
74
- let(:time) { Time.now.utc.iso8601(3) }
75
- let(:command) { Castle::Commands::GetDevice.build(device_token: '1') }
76
- let(:expected_body) { {} }
77
-
78
- before { allow(Castle::Utils::GetTimestamp).to receive(:call).and_return(time) }
79
-
80
- it { expect(build.body).to eql(expected_body.to_json) }
81
- it { expect(build.method).to eql('GET') }
82
- it { expect(build.path).to eql("/v1/#{command.path}") }
83
- end
84
-
85
- context 'when put' do
86
- let(:time) { Time.now.utc.iso8601(3) }
87
- let(:command) { Castle::Commands::ApproveDevice.build(device_token: '1') }
88
- let(:expected_body) { {} }
89
-
90
- before { allow(Castle::Utils::GetTimestamp).to receive(:call).and_return(time) }
91
-
92
- it { expect(build.body).to eql(expected_body.to_json) }
93
- it { expect(build.method).to eql('PUT') }
94
- it { expect(build.path).to eql("/v1/#{command.path}") }
95
- end
96
- end
97
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Failover::Strategy do
4
- subject(:strategy) { described_class }
5
-
6
- it { expect(strategy::ALLOW).to be(:allow) }
7
- it { expect(strategy::DENY).to be(:deny) }
8
- it { expect(strategy::CHALLENGE).to be(:challenge) }
9
- it { expect(strategy::THROW).to be(:throw) }
10
-
11
- it { expect(Castle::Failover::STRATEGIES).to eql(%i[allow deny challenge throw]) }
12
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Headers::Extract do
4
- subject(:extract_call) { described_class.new(formatted_headers).call }
5
-
6
- let(:formatted_headers) do
7
- {
8
- 'Content-Length' => '0',
9
- 'Authorization' => 'Basic 123456',
10
- 'Cookie' => '__cid=abcd;other=efgh',
11
- 'Ok' => 'OK',
12
- 'Accept' => 'application/json',
13
- 'X-Forwarded-For' => '1.2.3.4',
14
- 'User-Agent' => 'Mozilla 1234'
15
- }
16
- end
17
-
18
- after do
19
- Castle.config.allowlisted = %w[]
20
- Castle.config.denylisted = %w[]
21
- end
22
-
23
- context 'when allowlist is not set in the configuration' do
24
- let(:result) do
25
- {
26
- 'Accept' => 'application/json',
27
- 'Authorization' => true,
28
- 'Cookie' => true,
29
- 'Content-Length' => '0',
30
- 'Ok' => 'OK',
31
- 'User-Agent' => 'Mozilla 1234',
32
- 'X-Forwarded-For' => '1.2.3.4'
33
- }
34
- end
35
-
36
- it { expect(extract_call).to eq(result) }
37
- end
38
-
39
- context 'when allowlist is set in the configuration' do
40
- before { Castle.config.allowlisted = %w[Accept OK] }
41
-
42
- let(:result) do
43
- {
44
- 'Accept' => 'application/json',
45
- 'Authorization' => true,
46
- 'Cookie' => true,
47
- 'Content-Length' => true,
48
- 'Ok' => 'OK',
49
- 'User-Agent' => 'Mozilla 1234',
50
- 'X-Forwarded-For' => true
51
- }
52
- end
53
-
54
- it { expect(extract_call).to eq(result) }
55
- end
56
-
57
- context 'when denylist is set in the configuration' do
58
- context 'with a User-Agent' do
59
- let(:result) do
60
- {
61
- 'Accept' => 'application/json',
62
- 'Authorization' => true,
63
- 'Cookie' => true,
64
- 'Content-Length' => '0',
65
- 'Ok' => 'OK',
66
- 'User-Agent' => 'Mozilla 1234',
67
- 'X-Forwarded-For' => '1.2.3.4'
68
- }
69
- end
70
-
71
- before { Castle.config.denylisted = %w[User-Agent] }
72
-
73
- it { expect(extract_call).to eq(result) }
74
- end
75
-
76
- context 'with a different header' do
77
- let(:result) do
78
- {
79
- 'Accept' => true,
80
- 'Authorization' => true,
81
- 'Cookie' => true,
82
- 'Content-Length' => '0',
83
- 'Ok' => 'OK',
84
- 'User-Agent' => 'Mozilla 1234',
85
- 'X-Forwarded-For' => '1.2.3.4'
86
- }
87
- end
88
-
89
- before { Castle.config.denylisted = %w[Accept] }
90
-
91
- it { expect(extract_call).to eq(result) }
92
- end
93
- end
94
-
95
- context 'when a header is both allowlisted and denylisted' do
96
- before do
97
- Castle.config.allowlisted = %w[Accept]
98
- Castle.config.denylisted = %w[Accept]
99
- end
100
-
101
- it { expect(extract_call['Accept']).to be(true) }
102
- end
103
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Headers::Filter do
4
- subject(:filter_call) { described_class.new(request).call }
5
-
6
- let(:env) do
7
- result =
8
- Rack::MockRequest.env_for(
9
- '/',
10
- 'Action-Dispatch.request.content-Type' => 'application/json',
11
- 'HTTP_AUTHORIZATION' => 'Basic 123456',
12
- 'HTTP_COOKIE' => '__cid=abcd;other=efgh',
13
- 'HTTP_ACCEPT' => 'application/json',
14
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
15
- 'HTTP_USER_AGENT' => 'Mozilla 1234',
16
- 'TEST' => '1',
17
- 'REMOTE_ADDR' => '1.2.3.4',
18
- 'HTTP_CONTENT_LENGTH' => '0',
19
- 'http_accept_language.parser' => -> { 'noop' }
20
- )
21
- result[:HTTP_OK] = 'OK'
22
- result
23
- end
24
- let(:filtered) do
25
- {
26
- 'Accept' => 'application/json',
27
- 'Authorization' => 'Basic 123456',
28
- 'Cookie' => '__cid=abcd;other=efgh',
29
- 'Content-Length' => '0',
30
- 'Ok' => 'OK',
31
- 'User-Agent' => 'Mozilla 1234',
32
- 'Remote-Addr' => '1.2.3.4',
33
- 'X-Forwarded-For' => '1.2.3.4',
34
- 'Accept-Language.parser' => start_with('#<Proc')
35
- }
36
- end
37
- let(:request) { Rack::Request.new(env) }
38
-
39
- context 'with list of header' do
40
- it { expect(filter_call).to match(filtered) }
41
- end
42
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::Headers::Format do
4
- subject(:format) { described_class }
5
-
6
- it 'removes HTTP_' do
7
- expect(format.call('HTTP_X_TEST')).to eql('X-Test')
8
- end
9
-
10
- it 'capitalizes header' do
11
- expect(format.call('X_TEST')).to eql('X-Test')
12
- end
13
-
14
- it 'ignores letter case and -_ divider' do
15
- expect(format.call('http-X_teST')).to eql('X-Test')
16
- end
17
-
18
- it 'does not remove http if there is no _- char' do
19
- expect(format.call('httpX_teST')).to eql('Httpx-Test')
20
- end
21
-
22
- it 'capitalizes' do
23
- expect(format.call(:clearance)).to eql('Clearance')
24
- end
25
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Castle::IPs::Extract do
4
- subject(:extractor) { described_class.new(headers) }
5
-
6
- describe 'ip' do
7
- after do
8
- Castle.config.ip_headers = []
9
- Castle.config.trusted_proxies = []
10
- end
11
-
12
- context 'when regular ip' do
13
- let(:headers) { { 'X-Forwarded-For' => '1.2.3.5' } }
14
-
15
- it { expect(extractor.call).to eql('1.2.3.5') }
16
- end
17
-
18
- context 'when we need to use other ip header' do
19
- let(:headers) { { 'Cf-Connecting-Ip' => '1.2.3.4', 'X-Forwarded-For' => '1.1.1.1, 1.2.2.2, 1.2.3.5' } }
20
-
21
- context 'with uppercase format' do
22
- before { Castle.config.ip_headers = %w[CF_CONNECTING_IP X-Forwarded-For] }
23
-
24
- it { expect(extractor.call).to eql('1.2.3.4') }
25
- end
26
-
27
- context 'with regular format' do
28
- before { Castle.config.ip_headers = %w[Cf-Connecting-Ip X-Forwarded-For] }
29
-
30
- it { expect(extractor.call).to eql('1.2.3.4') }
31
- end
32
-
33
- context 'with value from trusted proxies it get seconds header' do
34
- before do
35
- Castle.config.ip_headers = %w[Cf-Connecting-Ip X-Forwarded-For]
36
- Castle.config.trusted_proxies = %w[1.2.3.4]
37
- end
38
-
39
- it { expect(extractor.call).to eql('1.2.3.5') }
40
- end
41
- end
42
-
43
- context 'with all the trusted proxies' do
44
- let(:http_x_header) { '127.0.0.1,10.0.0.1,172.31.0.1,192.168.0.1' }
45
-
46
- let(:headers) { { 'Remote-Addr' => '127.0.0.1', 'X-Forwarded-For' => http_x_header } }
47
-
48
- it 'fallbacks to first available header when all headers are marked trusted proxy' do
49
- expect(extractor.call).to eql('127.0.0.1')
50
- end
51
- end
52
-
53
- context 'with trust_proxy_chain option' do
54
- let(:http_x_header) { '6.6.6.6, 2.2.2.3, 6.6.6.5' }
55
-
56
- let(:headers) { { 'Remote-Addr' => '6.6.6.4', 'X-Forwarded-For' => http_x_header } }
57
-
58
- before { Castle.config.trust_proxy_chain = true }
59
-
60
- it 'selects first available header' do
61
- expect(extractor.call).to eql('6.6.6.6')
62
- end
63
- end
64
-
65
- context 'with trusted_proxy_depth option' do
66
- let(:http_x_header) { '6.6.6.6, 2.2.2.3, 6.6.6.5' }
67
-
68
- let(:headers) { { 'Remote-Addr' => '6.6.6.4', 'X-Forwarded-For' => http_x_header } }
69
-
70
- before { Castle.config.trusted_proxy_depth = 1 }
71
-
72
- it 'selects first available header' do
73
- expect(extractor.call).to eql('2.2.2.3')
74
- end
75
- end
76
-
77
- context 'when list of not trusted ips provided in X_FORWARDED_FOR' do
78
- let(:headers) { { 'X-Forwarded-For' => '6.6.6.6, 2.2.2.3, 192.168.0.7', 'Client-Ip' => '6.6.6.6' } }
79
-
80
- it 'does not allow to spoof ip' do
81
- expect(extractor.call).to eql('2.2.2.3')
82
- end
83
-
84
- context 'when marked 2.2.2.3 as trusted proxy' do
85
- before { Castle.config.trusted_proxies = [/^2.2.2.\d$/] }
86
-
87
- it { expect(extractor.call).to eql('6.6.6.6') }
88
- end
89
- end
90
- end
91
- end