castle-rb 5.0.0 → 7.1.1

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -39
  3. data/lib/castle.rb +49 -29
  4. data/lib/castle/api.rb +20 -16
  5. data/lib/castle/api/approve_device.rb +20 -0
  6. data/lib/castle/api/authenticate.rb +37 -0
  7. data/lib/castle/api/end_impersonation.rb +24 -0
  8. data/lib/castle/api/filter.rb +37 -0
  9. data/lib/castle/api/get_device.rb +20 -0
  10. data/lib/castle/api/get_devices_for_user.rb +20 -0
  11. data/lib/castle/api/log.rb +37 -0
  12. data/lib/castle/api/report_device.rb +20 -0
  13. data/lib/castle/api/risk.rb +37 -0
  14. data/lib/castle/api/start_impersonation.rb +24 -0
  15. data/lib/castle/api/track.rb +21 -0
  16. data/lib/castle/client.rb +74 -68
  17. data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
  18. data/lib/castle/commands/approve_device.rb +17 -0
  19. data/lib/castle/commands/authenticate.rb +13 -13
  20. data/lib/castle/commands/end_impersonation.rb +25 -0
  21. data/lib/castle/commands/filter.rb +22 -0
  22. data/lib/castle/commands/get_device.rb +17 -0
  23. data/lib/castle/commands/get_devices_for_user.rb +17 -0
  24. data/lib/castle/commands/log.rb +22 -0
  25. data/lib/castle/commands/report_device.rb +17 -0
  26. data/lib/castle/commands/risk.rb +22 -0
  27. data/lib/castle/commands/start_impersonation.rb +25 -0
  28. data/lib/castle/commands/track.rb +12 -13
  29. data/lib/castle/configuration.rb +31 -23
  30. data/lib/castle/context/{default.rb → get_default.rb} +5 -6
  31. data/lib/castle/context/{merger.rb → merge.rb} +3 -3
  32. data/lib/castle/context/prepare.rb +18 -0
  33. data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
  34. data/lib/castle/core/get_connection.rb +27 -0
  35. data/lib/castle/{api/response.rb → core/process_response.rb} +8 -3
  36. data/lib/castle/core/process_webhook.rb +25 -0
  37. data/lib/castle/core/send_request.rb +42 -0
  38. data/lib/castle/errors.rb +38 -12
  39. data/lib/castle/failover/prepare_response.rb +28 -0
  40. data/lib/castle/failover/strategy.rb +23 -0
  41. data/lib/castle/{extractors/headers.rb → headers/extract.rb} +8 -6
  42. data/lib/castle/headers/filter.rb +40 -0
  43. data/lib/castle/headers/format.rb +24 -0
  44. data/lib/castle/{extractors/ip.rb → ips/extract.rb} +11 -7
  45. data/lib/castle/logger.rb +19 -0
  46. data/lib/castle/payload/prepare.rb +26 -0
  47. data/lib/castle/secure_mode.rb +7 -2
  48. data/lib/castle/session.rb +18 -0
  49. data/lib/castle/singleton_configuration.rb +9 -0
  50. data/lib/castle/support/hanami.rb +2 -6
  51. data/lib/castle/support/rails.rb +1 -3
  52. data/lib/castle/utils/clean_invalid_chars.rb +22 -0
  53. data/lib/castle/utils/clone.rb +15 -0
  54. data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
  55. data/lib/castle/utils/get_timestamp.rb +15 -0
  56. data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
  57. data/lib/castle/utils/secure_compare.rb +22 -0
  58. data/lib/castle/validators/not_supported.rb +1 -0
  59. data/lib/castle/validators/present.rb +1 -0
  60. data/lib/castle/verdict.rb +15 -0
  61. data/lib/castle/version.rb +1 -1
  62. data/lib/castle/webhooks/verify.rb +45 -0
  63. data/spec/integration/rails/rails_spec.rb +42 -14
  64. data/spec/integration/rails/support/application.rb +3 -1
  65. data/spec/integration/rails/support/home_controller.rb +50 -6
  66. data/spec/lib/castle/api/approve_device_spec.rb +21 -0
  67. data/spec/lib/castle/api/authenticate_spec.rb +136 -0
  68. data/spec/lib/castle/api/end_impersonation_spec.rb +65 -0
  69. data/spec/lib/castle/api/filter_spec.rb +5 -0
  70. data/spec/lib/castle/api/get_device_spec.rb +19 -0
  71. data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
  72. data/spec/lib/castle/api/log_spec.rb +5 -0
  73. data/spec/lib/castle/api/report_device_spec.rb +21 -0
  74. data/spec/lib/castle/api/risk_spec.rb +5 -0
  75. data/spec/lib/castle/api/start_impersonation_spec.rb +65 -0
  76. data/spec/lib/castle/api/track_spec.rb +72 -0
  77. data/spec/lib/castle/api_spec.rb +14 -15
  78. data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +6 -15
  79. data/spec/lib/castle/client_spec.rb +110 -92
  80. data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
  81. data/spec/lib/castle/commands/authenticate_spec.rb +15 -31
  82. data/spec/lib/castle/commands/end_impersonation_spec.rb +79 -0
  83. data/spec/lib/castle/commands/filter_spec.rb +72 -0
  84. data/spec/lib/castle/commands/get_device_spec.rb +24 -0
  85. data/spec/lib/castle/commands/{review_spec.rb → get_devices_for_user_spec.rb} +7 -7
  86. data/spec/lib/castle/commands/log_spec.rb +73 -0
  87. data/spec/lib/castle/commands/report_device_spec.rb +24 -0
  88. data/spec/lib/castle/commands/risk_spec.rb +73 -0
  89. data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +13 -41
  90. data/spec/lib/castle/commands/track_spec.rb +14 -34
  91. data/spec/lib/castle/configuration_spec.rb +8 -141
  92. data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +9 -10
  93. data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
  94. data/spec/lib/castle/context/prepare_spec.rb +43 -0
  95. data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
  96. data/spec/lib/castle/core/get_connection_spec.rb +43 -0
  97. data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +49 -1
  98. data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
  99. data/spec/lib/castle/{api/request_spec.rb → core/send_request_spec.rb} +16 -37
  100. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  101. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +7 -9
  102. data/spec/lib/castle/headers/filter_spec.rb +39 -0
  103. data/spec/lib/castle/headers/format_spec.rb +25 -0
  104. data/spec/lib/castle/{extractors/ip_spec.rb → ips/extract_spec.rb} +5 -14
  105. data/spec/lib/castle/logger_spec.rb +38 -0
  106. data/spec/lib/castle/payload/prepare_spec.rb +55 -0
  107. data/spec/lib/castle/session_spec.rb +65 -0
  108. data/spec/lib/castle/singleton_configuration_spec.rb +14 -0
  109. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
  110. data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
  111. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
  112. data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
  113. data/spec/lib/castle/utils/merge_spec.rb +15 -0
  114. data/spec/lib/castle/validators/present_spec.rb +5 -6
  115. data/spec/lib/castle/verdict_spec.rb +9 -0
  116. data/spec/lib/castle/webhooks/verify_spec.rb +53 -0
  117. data/spec/lib/castle_spec.rb +4 -10
  118. data/spec/spec_helper.rb +3 -3
  119. data/spec/support/shared_examples/action_request.rb +155 -0
  120. data/spec/support/shared_examples/configuration.rb +101 -0
  121. metadata +144 -67
  122. data/lib/castle/api/connection.rb +0 -24
  123. data/lib/castle/api/request.rb +0 -42
  124. data/lib/castle/api/session.rb +0 -20
  125. data/lib/castle/commands/identify.rb +0 -23
  126. data/lib/castle/commands/impersonate.rb +0 -26
  127. data/lib/castle/commands/review.rb +0 -14
  128. data/lib/castle/events.rb +0 -49
  129. data/lib/castle/failover_auth_response.rb +0 -21
  130. data/lib/castle/headers_filter.rb +0 -35
  131. data/lib/castle/headers_formatter.rb +0 -22
  132. data/lib/castle/review.rb +0 -11
  133. data/lib/castle/utils.rb +0 -55
  134. data/lib/castle/utils/cloner.rb +0 -11
  135. data/lib/castle/utils/timestamp.rb +0 -12
  136. data/spec/lib/castle/api/connection_spec.rb +0 -59
  137. data/spec/lib/castle/api/session_spec.rb +0 -86
  138. data/spec/lib/castle/commands/identify_spec.rb +0 -88
  139. data/spec/lib/castle/events_spec.rb +0 -5
  140. data/spec/lib/castle/headers_filter_spec.rb +0 -38
  141. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  142. data/spec/lib/castle/review_spec.rb +0 -19
  143. data/spec/lib/castle/utils/merger_spec.rb +0 -13
  144. data/spec/lib/castle/utils_spec.rb +0 -156
@@ -3,35 +3,34 @@
3
3
  describe Castle::API do
4
4
  subject(:call) { described_class.call(command) }
5
5
 
6
- let(:command) { Castle::Commands::Track.new({}).build(event: '$login.succeeded') }
6
+ let(:command) { Castle::Commands::Track.build(event: '$login.succeeded') }
7
7
 
8
8
  context 'when request timeouts' do
9
9
  before { stub_request(:any, /api.castle.io/).to_timeout }
10
10
 
11
- it do
12
- expect do
13
- call
14
- end.to raise_error(Castle::RequestError)
15
- end
11
+ it { expect { call }.to raise_error(Castle::RequestError) }
16
12
  end
17
13
 
18
14
  context 'when non-OK response code' do
19
15
  before { stub_request(:any, /api.castle.io/).to_return(status: 400) }
20
16
 
21
- it do
22
- expect do
23
- call
24
- end.to raise_error(Castle::BadRequestError)
25
- end
17
+ it { expect { call }.to raise_error(Castle::BadRequestError) }
26
18
  end
27
19
 
28
20
  context 'when no api_secret' do
29
21
  before { allow(Castle.config).to receive(:api_secret).and_return('') }
30
22
 
31
- it do
32
- expect do
33
- call
34
- end.to raise_error(Castle::ConfigurationError)
23
+ it { expect { call }.to raise_error(Castle::ConfigurationError) }
24
+ end
25
+
26
+ context 'when custom config' do
27
+ let(:config) { Castle::Configuration.new }
28
+
29
+ before do
30
+ config.api_secret = 'test'
31
+ stub_request(:any, /api.castle.io/)
35
32
  end
33
+
34
+ it { expect { call }.not_to raise_error }
36
35
  end
37
36
  end
@@ -1,16 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Castle::Extractors::ClientId do
3
+ describe Castle::ClientId::Extract do
4
4
  subject(:extractor) { described_class.new(formatted_headers, cookies) }
5
5
 
6
- let(:formatted_headers) { Castle::HeadersFilter.new(request).call }
6
+ let(:formatted_headers) { Castle::Headers::Filter.new(request).call }
7
7
  let(:client_id_cookie) { 'abcd' }
8
8
  let(:client_id_header) { 'abcde' }
9
9
  let(:cookies) { request.cookies }
10
10
  let(:request) { Rack::Request.new(env) }
11
- let(:env) do
12
- Rack::MockRequest.env_for('/', headers)
13
- end
11
+ let(:env) { Rack::MockRequest.env_for('/', headers) }
14
12
 
15
13
  context 'with client_id' do
16
14
  let(:headers) do
@@ -20,17 +18,12 @@ describe Castle::Extractors::ClientId do
20
18
  }
21
19
  end
22
20
 
23
- it do
24
- expect(extractor.call).to eql(client_id_cookie)
25
- end
21
+ it { expect(extractor.call).to eql(client_id_cookie) }
26
22
  end
27
23
 
28
24
  context 'with X-Castle-Client-Id header' do
29
25
  let(:headers) do
30
- {
31
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
32
- 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header
33
- }
26
+ { 'HTTP_X_FORWARDED_FOR' => '1.2.3.4', 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header }
34
27
  end
35
28
 
36
29
  it 'appends the client_id' do
@@ -42,9 +35,7 @@ describe Castle::Extractors::ClientId do
42
35
  let(:cookies) { nil }
43
36
  let(:headers) { {} }
44
37
 
45
- it do
46
- expect(extractor.call).to eql('')
47
- end
38
+ it { expect(extractor.call).to eql('') }
48
39
  end
49
40
 
50
41
  context 'with X-Castle-Client-Id header and cookies client' do
@@ -14,26 +14,26 @@ describe Castle::Client do
14
14
  end
15
15
  let(:request) { Rack::Request.new(env) }
16
16
  let(:client) { described_class.from_request(request) }
17
- let(:request_to_context) { described_class.to_context(request) }
17
+ let(:request_to_context) { Castle::Context::Prepare.call(request) }
18
18
  let(:client_with_user_timestamp) do
19
- described_class.new(request_to_context, timestamp: time_user)
19
+ described_class.new(context: request_to_context, timestamp: time_user)
20
20
  end
21
- let(:client_with_no_timestamp) { described_class.new(request_to_context) }
21
+ let(:client_with_no_timestamp) { described_class.new(context: request_to_context) }
22
22
 
23
23
  let(:headers) do
24
- {
25
- 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true
26
- }
24
+ { 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true }
27
25
  end
28
26
  let(:context) do
29
27
  {
30
28
  client_id: 'abcd',
31
29
  active: true,
32
- origin: 'web',
33
30
  user_agent: ua,
34
31
  headers: headers,
35
32
  ip: ip,
36
- library: { name: 'castle-rb', version: '2.2.0' }
33
+ library: {
34
+ name: 'castle-rb',
35
+ version: '2.2.0'
36
+ }
37
37
  }
38
38
  end
39
39
 
@@ -45,53 +45,43 @@ describe Castle::Client do
45
45
  before do
46
46
  Timecop.freeze(time_now)
47
47
  stub_const('Castle::VERSION', '2.2.0')
48
- stub_request(:any, /api.castle.io/).with(
49
- basic_auth: ['', 'secret']
50
- ).to_return(status: 200, body: response_body, headers: {})
48
+ stub_request(:any, /api.castle.io/)
49
+ .with(basic_auth: ['', 'secret'])
50
+ .to_return(status: 200, body: response_body, headers: {})
51
51
  end
52
52
 
53
53
  after { Timecop.return }
54
54
 
55
55
  describe 'parses the request' do
56
- before do
57
- allow(Castle::API).to receive(:request).and_call_original
58
- end
56
+ before { allow(Castle::API).to receive(:send_request).and_call_original }
59
57
 
60
58
  it do
61
59
  client.authenticate(event: '$login.succeeded', user_id: '1234')
62
- expect(Castle::API).to have_received(:request)
63
- end
64
- end
65
-
66
- describe 'to_context' do
67
- it do
68
- expect(described_class.to_context(request)).to eql(context)
60
+ expect(Castle::API).to have_received(:send_request)
69
61
  end
70
62
  end
71
63
 
72
- describe 'to_options' do
73
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
74
- let(:result) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_auto } }
75
-
76
- it do
77
- expect(described_class.to_options(options)).to eql(result)
78
- end
79
- end
80
-
81
- describe 'impersonate' do
64
+ describe 'end impersonation' do
82
65
  let(:impersonator) { 'test@castle.io' }
83
66
  let(:request_body) do
84
- { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
85
- properties: { impersonator: impersonator }, context: context }
67
+ {
68
+ user_id: '1234',
69
+ timestamp: time_auto,
70
+ sent_at: time_auto,
71
+ properties: {
72
+ impersonator: impersonator
73
+ },
74
+ context: context
75
+ }
86
76
  end
87
77
  let(:response_body) { { success: true }.to_json }
88
78
  let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
89
79
 
90
80
  context 'when used with symbol keys' do
91
- before { client.impersonate(options) }
81
+ before { client.end_impersonation(options) }
92
82
 
93
83
  it do
94
- assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
84
+ assert_requested :delete, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
95
85
  JSON.parse(req.body) == JSON.parse(request_body.to_json)
96
86
  end
97
87
  end
@@ -100,64 +90,43 @@ describe Castle::Client do
100
90
  context 'when request is not successful' do
101
91
  let(:response_body) { {}.to_json }
102
92
 
103
- it { expect { client.impersonate(options) }.to raise_error(Castle::ImpersonationFailed) }
93
+ it do
94
+ expect { client.end_impersonation(options) }.to raise_error(Castle::ImpersonationFailed)
95
+ end
104
96
  end
105
97
  end
106
98
 
107
- describe 'identify' do
99
+ describe 'start impersonation' do
100
+ let(:impersonator) { 'test@castle.io' }
108
101
  let(:request_body) do
109
- { user_id: '1234', timestamp: time_auto,
110
- sent_at: time_auto, context: context, user_traits: { name: 'Jo' } }
102
+ {
103
+ user_id: '1234',
104
+ timestamp: time_auto,
105
+ sent_at: time_auto,
106
+ properties: {
107
+ impersonator: impersonator
108
+ },
109
+ context: context
110
+ }
111
111
  end
112
-
113
- before { client.identify(options) }
112
+ let(:response_body) { { success: true }.to_json }
113
+ let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
114
114
 
115
115
  context 'when used with symbol keys' do
116
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
116
+ before { client.start_impersonation(options) }
117
117
 
118
118
  it do
119
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
119
+ assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
120
120
  JSON.parse(req.body) == JSON.parse(request_body.to_json)
121
121
  end
122
122
  end
123
-
124
- context 'when passed timestamp in options and no defined timestamp' do
125
- let(:client) { client_with_no_timestamp }
126
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_user } }
127
- let(:request_body) do
128
- { user_id: '1234', user_traits: { name: 'Jo' }, context: context,
129
- timestamp: time_user, sent_at: time_auto }
130
- end
131
-
132
- it do
133
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
134
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
135
- end
136
- end
137
- end
138
-
139
- context 'with client initialized with timestamp' do
140
- let(:client) { client_with_user_timestamp }
141
- let(:request_body) do
142
- { user_id: '1234', timestamp: time_user, sent_at: time_auto,
143
- context: context, user_traits: { name: 'Jo' } }
144
- end
145
-
146
- it do
147
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
148
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
149
- end
150
- end
151
- end
152
123
  end
153
124
 
154
- context 'when used with string keys' do
155
- let(:options) { { 'user_id' => '1234', 'user_traits' => { 'name' => 'Jo' } } }
125
+ context 'when request is not successful' do
126
+ let(:response_body) { {}.to_json }
156
127
 
157
128
  it do
158
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
159
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
160
- end
129
+ expect { client.start_impersonation(options) }.to raise_error(Castle::ImpersonationFailed)
161
130
  end
162
131
  end
163
132
  end
@@ -166,8 +135,13 @@ describe Castle::Client do
166
135
  let(:options) { { event: '$login.succeeded', user_id: '1234' } }
167
136
  let(:request_response) { client.authenticate(options) }
168
137
  let(:request_body) do
169
- { event: '$login.succeeded', user_id: '1234', context: context,
170
- timestamp: time_auto, sent_at: time_auto }
138
+ {
139
+ event: '$login.succeeded',
140
+ user_id: '1234',
141
+ context: context,
142
+ timestamp: time_auto,
143
+ sent_at: time_auto
144
+ }
171
145
  end
172
146
 
173
147
  context 'when used with symbol keys' do
@@ -183,8 +157,13 @@ describe Castle::Client do
183
157
  let(:client) { client_with_no_timestamp }
184
158
  let(:options) { { event: '$login.succeeded', user_id: '1234', timestamp: time_user } }
185
159
  let(:request_body) do
186
- { event: '$login.succeeded', user_id: '1234', context: context,
187
- timestamp: time_user, sent_at: time_auto }
160
+ {
161
+ event: '$login.succeeded',
162
+ user_id: '1234',
163
+ context: context,
164
+ timestamp: time_user,
165
+ sent_at: time_auto
166
+ }
188
167
  end
189
168
 
190
169
  it do
@@ -197,8 +176,13 @@ describe Castle::Client do
197
176
  context 'with client initialized with timestamp' do
198
177
  let(:client) { client_with_user_timestamp }
199
178
  let(:request_body) do
200
- { event: '$login.succeeded', user_id: '1234', context: context,
201
- timestamp: time_user, sent_at: time_auto }
179
+ {
180
+ event: '$login.succeeded',
181
+ user_id: '1234',
182
+ context: context,
183
+ timestamp: time_user,
184
+ sent_at: time_auto
185
+ }
202
186
  end
203
187
 
204
188
  it do
@@ -241,7 +225,8 @@ describe Castle::Client do
241
225
  end
242
226
 
243
227
  it { assert_not_requested :post, 'https://api.castle.io/v1/authenticate' }
244
- it { expect(request_response[:action]).to be_eql('allow') }
228
+ it { expect(request_response[:policy][:action]).to be_eql(Castle::Verdict::ALLOW) }
229
+ it { expect(request_response[:action]).to be_eql(Castle::Verdict::ALLOW) }
245
230
  it { expect(request_response[:user_id]).to be_eql('1234') }
246
231
  it { expect(request_response[:failover]).to be true }
247
232
  it { expect(request_response[:failover_reason]).to be_eql('Castle is set to do not track.') }
@@ -249,7 +234,9 @@ describe Castle::Client do
249
234
 
250
235
  context 'when request with fail' do
251
236
  before do
252
- allow(Castle::API).to receive(:request).and_raise(Castle::RequestError.new(Timeout::Error))
237
+ allow(Castle::API).to receive(:send_request).and_raise(
238
+ Castle::RequestError.new(Timeout::Error)
239
+ )
253
240
  end
254
241
 
255
242
  context 'with request error and throw strategy' do
@@ -260,6 +247,7 @@ describe Castle::Client do
260
247
 
261
248
  context 'with request error and not throw on eg deny strategy' do
262
249
  it { assert_not_requested :post, 'https://:secret@api.castle.io/v1/authenticate' }
250
+ it { expect(request_response[:policy][:action]).to be_eql('allow') }
263
251
  it { expect(request_response[:action]).to be_eql('allow') }
264
252
  it { expect(request_response[:user_id]).to be_eql('1234') }
265
253
  it { expect(request_response[:failover]).to be true }
@@ -268,7 +256,9 @@ describe Castle::Client do
268
256
  end
269
257
 
270
258
  context 'when request is internal server error' do
271
- before { allow(Castle::API).to receive(:request).and_raise(Castle::InternalServerError) }
259
+ before do
260
+ allow(Castle::API).to receive(:send_request).and_raise(Castle::InternalServerError)
261
+ end
272
262
 
273
263
  describe 'throw strategy' do
274
264
  before { allow(Castle.config).to receive(:failover_strategy).and_return(:throw) }
@@ -278,6 +268,7 @@ describe Castle::Client do
278
268
 
279
269
  describe 'not throw on eg deny strategy' do
280
270
  it { assert_not_requested :post, 'https://:secret@api.castle.io/v1/authenticate' }
271
+ it { expect(request_response[:policy][:action]).to be_eql('allow') }
281
272
  it { expect(request_response[:action]).to be_eql('allow') }
282
273
  it { expect(request_response[:user_id]).to be_eql('1234') }
283
274
  it { expect(request_response[:failover]).to be true }
@@ -288,8 +279,13 @@ describe Castle::Client do
288
279
 
289
280
  describe 'track' do
290
281
  let(:request_body) do
291
- { event: '$login.succeeded', context: context, user_id: '1234',
292
- timestamp: time_auto, sent_at: time_auto }
282
+ {
283
+ event: '$login.succeeded',
284
+ context: context,
285
+ user_id: '1234',
286
+ timestamp: time_auto,
287
+ sent_at: time_auto
288
+ }
293
289
  end
294
290
 
295
291
  before { client.track(options) }
@@ -307,8 +303,13 @@ describe Castle::Client do
307
303
  let(:client) { client_with_no_timestamp }
308
304
  let(:options) { { event: '$login.succeeded', user_id: '1234', timestamp: time_user } }
309
305
  let(:request_body) do
310
- { event: '$login.succeeded', user_id: '1234', context: context,
311
- timestamp: time_user, sent_at: time_auto }
306
+ {
307
+ event: '$login.succeeded',
308
+ user_id: '1234',
309
+ context: context,
310
+ timestamp: time_user,
311
+ sent_at: time_auto
312
+ }
312
313
  end
313
314
 
314
315
  it do
@@ -321,8 +322,13 @@ describe Castle::Client do
321
322
  context 'with client initialized with timestamp' do
322
323
  let(:client) { client_with_user_timestamp }
323
324
  let(:request_body) do
324
- { event: '$login.succeeded', context: context, user_id: '1234',
325
- timestamp: time_user, sent_at: time_auto }
325
+ {
326
+ event: '$login.succeeded',
327
+ context: context,
328
+ user_id: '1234',
329
+ timestamp: time_user,
330
+ sent_at: time_auto
331
+ }
326
332
  end
327
333
 
328
334
  it do
@@ -357,4 +363,16 @@ describe Castle::Client do
357
363
  it { expect(client).to be_tracked }
358
364
  end
359
365
  end
366
+
367
+ describe 'filter' do
368
+ it_behaves_like 'action request', :filter
369
+ end
370
+
371
+ describe 'risk' do
372
+ it_behaves_like 'action request', :risk
373
+ end
374
+
375
+ describe 'log' do
376
+ it_behaves_like 'action request', :log
377
+ end
360
378
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Commands::ApproveDevice do
4
+ subject(:instance) { described_class }
5
+
6
+ let(:context) { {} }
7
+ let(:device_token) { '1234' }
8
+
9
+ describe '.build' do
10
+ subject(:command) { instance.build(device_token: device_token) }
11
+
12
+ context 'without device_token' do
13
+ let(:device_token) { '' }
14
+
15
+ it { expect { command }.to raise_error(Castle::InvalidParametersError) }
16
+ end
17
+
18
+ context 'with device_token' do
19
+ it { expect(command.method).to be_eql(:put) }
20
+ it { expect(command.path).to be_eql("devices/#{device_token}/approve") }
21
+ it { expect(command.data).to be_nil }
22
+ end
23
+ end
24
+ end