castle-rb 5.0.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -33
  3. data/lib/castle.rb +46 -22
  4. data/lib/castle/api.rb +22 -13
  5. data/lib/castle/api/approve_device.rb +25 -0
  6. data/lib/castle/api/authenticate.rb +34 -0
  7. data/lib/castle/api/end_impersonation.rb +29 -0
  8. data/lib/castle/api/get_device.rb +25 -0
  9. data/lib/castle/api/get_devices_for_user.rb +25 -0
  10. data/lib/castle/api/identify.rb +26 -0
  11. data/lib/castle/api/report_device.rb +25 -0
  12. data/lib/castle/api/review.rb +24 -0
  13. data/lib/castle/api/start_impersonation.rb +29 -0
  14. data/lib/castle/api/track.rb +26 -0
  15. data/lib/castle/client.rb +48 -62
  16. data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
  17. data/lib/castle/commands/approve_device.rb +21 -0
  18. data/lib/castle/commands/authenticate.rb +13 -13
  19. data/lib/castle/commands/end_impersonation.rb +25 -0
  20. data/lib/castle/commands/get_device.rb +21 -0
  21. data/lib/castle/commands/get_devices_for_user.rb +21 -0
  22. data/lib/castle/commands/identify.rb +12 -13
  23. data/lib/castle/commands/report_device.rb +21 -0
  24. data/lib/castle/commands/review.rb +6 -3
  25. data/lib/castle/commands/start_impersonation.rb +25 -0
  26. data/lib/castle/commands/track.rb +12 -13
  27. data/lib/castle/configuration.rb +17 -19
  28. data/lib/castle/context/{default.rb → get_default.rb} +5 -6
  29. data/lib/castle/context/{merger.rb → merge.rb} +3 -3
  30. data/lib/castle/context/prepare.rb +18 -0
  31. data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
  32. data/lib/castle/core/get_connection.rb +25 -0
  33. data/lib/castle/{api/response.rb → core/process_response.rb} +4 -2
  34. data/lib/castle/core/process_webhook.rb +20 -0
  35. data/lib/castle/core/send_request.rb +50 -0
  36. data/lib/castle/errors.rb +2 -0
  37. data/lib/castle/events.rb +1 -1
  38. data/lib/castle/failover/prepare_response.rb +23 -0
  39. data/lib/castle/failover/strategy.rb +20 -0
  40. data/lib/castle/{extractors/headers.rb → headers/extract.rb} +8 -6
  41. data/lib/castle/headers/filter.rb +37 -0
  42. data/lib/castle/headers/format.rb +24 -0
  43. data/lib/castle/{extractors/ip.rb → ip/extract.rb} +8 -7
  44. data/lib/castle/logger.rb +19 -0
  45. data/lib/castle/payload/prepare.rb +27 -0
  46. data/lib/castle/secure_mode.rb +6 -2
  47. data/lib/castle/session.rb +18 -0
  48. data/lib/castle/singleton_configuration.rb +9 -0
  49. data/lib/castle/utils/clean_invalid_chars.rb +24 -0
  50. data/lib/castle/utils/clone.rb +15 -0
  51. data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
  52. data/lib/castle/utils/get_timestamp.rb +15 -0
  53. data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
  54. data/lib/castle/utils/secure_compare.rb +22 -0
  55. data/lib/castle/validators/not_supported.rb +1 -0
  56. data/lib/castle/validators/present.rb +1 -0
  57. data/lib/castle/verdict.rb +13 -0
  58. data/lib/castle/version.rb +1 -1
  59. data/lib/castle/webhooks/verify.rb +43 -0
  60. data/spec/integration/rails/rails_spec.rb +33 -7
  61. data/spec/integration/rails/support/application.rb +3 -1
  62. data/spec/integration/rails/support/home_controller.rb +47 -5
  63. data/spec/lib/castle/api/approve_device_spec.rb +21 -0
  64. data/spec/lib/castle/api/authenticate_spec.rb +140 -0
  65. data/spec/lib/castle/api/end_impersonation_spec.rb +59 -0
  66. data/spec/lib/castle/api/get_device_spec.rb +19 -0
  67. data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
  68. data/spec/lib/castle/api/identify_spec.rb +68 -0
  69. data/spec/lib/castle/api/report_device_spec.rb +21 -0
  70. data/spec/lib/castle/{review_spec.rb → api/review_spec.rb} +3 -3
  71. data/spec/lib/castle/api/start_impersonation_spec.rb +59 -0
  72. data/spec/lib/castle/api/track_spec.rb +68 -0
  73. data/spec/lib/castle/api_spec.rb +16 -1
  74. data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +2 -2
  75. data/spec/lib/castle/client_spec.rb +39 -21
  76. data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
  77. data/spec/lib/castle/commands/authenticate_spec.rb +7 -16
  78. data/spec/lib/castle/commands/end_impersonation_spec.rb +82 -0
  79. data/spec/lib/castle/commands/get_device_spec.rb +24 -0
  80. data/spec/lib/castle/commands/get_devices_for_user_spec.rb +24 -0
  81. data/spec/lib/castle/commands/identify_spec.rb +5 -16
  82. data/spec/lib/castle/commands/report_device_spec.rb +24 -0
  83. data/spec/lib/castle/commands/review_spec.rb +1 -1
  84. data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +7 -32
  85. data/spec/lib/castle/commands/track_spec.rb +5 -16
  86. data/spec/lib/castle/configuration_spec.rb +9 -138
  87. data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +1 -2
  88. data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
  89. data/spec/lib/castle/context/prepare_spec.rb +44 -0
  90. data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
  91. data/spec/lib/castle/{api/connection_spec.rb → core/get_connection_spec.rb} +3 -3
  92. data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +56 -1
  93. data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
  94. data/spec/lib/castle/{api/request_spec.rb → core/send_request_spec.rb} +20 -16
  95. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  96. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +7 -7
  97. data/spec/lib/castle/{headers_filter_spec.rb → headers/filter_spec.rb} +3 -3
  98. data/spec/lib/castle/headers/format_spec.rb +25 -0
  99. data/spec/lib/castle/{extractors/ip_spec.rb → ip/extract_spec.rb} +1 -1
  100. data/spec/lib/castle/logger_spec.rb +42 -0
  101. data/spec/lib/castle/payload/prepare_spec.rb +54 -0
  102. data/spec/lib/castle/{api/session_spec.rb → session_spec.rb} +6 -4
  103. data/spec/lib/castle/singleton_configuration_spec.rb +18 -0
  104. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
  105. data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
  106. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
  107. data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
  108. data/spec/lib/castle/utils/{merger_spec.rb → merge_spec.rb} +3 -3
  109. data/spec/lib/castle/verdict_spec.rb +9 -0
  110. data/spec/lib/castle/webhooks/verify_spec.rb +69 -0
  111. data/spec/spec_helper.rb +2 -0
  112. data/spec/support/shared_examples/configuration.rb +129 -0
  113. metadata +129 -57
  114. data/lib/castle/api/connection.rb +0 -24
  115. data/lib/castle/api/request.rb +0 -42
  116. data/lib/castle/api/session.rb +0 -20
  117. data/lib/castle/commands/impersonate.rb +0 -26
  118. data/lib/castle/failover_auth_response.rb +0 -21
  119. data/lib/castle/headers_filter.rb +0 -35
  120. data/lib/castle/headers_formatter.rb +0 -22
  121. data/lib/castle/review.rb +0 -11
  122. data/lib/castle/utils.rb +0 -55
  123. data/lib/castle/utils/cloner.rb +0 -11
  124. data/lib/castle/utils/timestamp.rb +0 -12
  125. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  126. data/spec/lib/castle/utils_spec.rb +0 -156
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 441cbae1aed67e419bff1683c41183abb10c3890d5a159f3365c0b4bd3855f0b
4
- data.tar.gz: 6ef7663b21e2de6b1ef833917dd4b62ee1891f017e33f88a1cb57252e5c3bcc4
3
+ metadata.gz: 5e4354fd7a96d78e03da9bbe10015e375af34636a2e5e186c4de361d0851a6ca
4
+ data.tar.gz: 15c6bbf6f34ba315b5f42db490754a92e238f66a006a91e3986b9b1ba540d47a
5
5
  SHA512:
6
- metadata.gz: b54200b206ea055878d2e4a6b46f82960cda186c4b57009765c7e4c8cce28c7f00c66ad216840f8fa5412a7ed876224f285a18388620bf7df9201425a0efb055
7
- data.tar.gz: cf9f8b5019377138fe5e61ced02b6323aa27464dc117c521fe560a6832115416bbd7e92a64a4710b96939adcd49af12feb538f3d24392d9654b14a21a48021a5
6
+ metadata.gz: 7bb580056e70ed854014054791ef5987874e52b2471838605a90092f70269ba095d40ef5df5310636b0fbd652dfa9460bb95a314e6e2dae5074732672c77122b
7
+ data.tar.gz: 22fb6013ec018e8c983ecf8051041eb62f69f4b54f73528f185f783ab2343e23f5504e3d420143fb3a19f0f49e094a2d56c2fc873411d697b95be645b7cefb47
data/README.md CHANGED
@@ -62,8 +62,14 @@ Castle.configure do |config|
62
62
  # For authenticate method you can set failover strategies: allow(default), deny, challenge, throw
63
63
  config.failover_strategy = :deny
64
64
 
65
- # Castle::RequestError is raised when timing out in milliseconds (default: 500 milliseconds)
66
- config.request_timeout = 2000
65
+ # Castle::RequestError is raised when timing out in milliseconds (default: 1000 milliseconds)
66
+ config.request_timeout = 1500
67
+
68
+ # Base Castle API url
69
+ # config.base_url = "https://api.castle.io/v1"
70
+
71
+ # Logger (need to respond to info method) - logs Castle API requests and responses
72
+ # config.logger = Logger.new(STDOUT)
67
73
 
68
74
  # Allowlisted and Denylisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
69
75
  # Allowlisted headers
@@ -95,13 +101,14 @@ Castle.configure do |config|
95
101
  # Sometimes, Cloud providers do not use consistent IP addresses to proxy requests.
96
102
  # In this case, the client IP is usually preserved in a custom header. Example:
97
103
  # Cloudflare preserves the client request in the 'Cf-Connecting-Ip' header.
98
- # It would be used like so: configuration.ip_headers=['Cf-Connecting-Ip']
99
- configuration.ip_headers = []
104
+ # It would be used like so: config.ip_headers=['Cf-Connecting-Ip']
105
+ config.ip_headers = []
100
106
 
101
107
  # If the specified header or X-Forwarded-For default contains a proxy chain with public IP addresses,
102
- # then one of the following must be set
103
- # 1. The trusted_proxies value must match the known proxy IP's
104
- # 2. The trusted_proxy_depth value must be set to the number of known trusted proxies in the chain (see below)
108
+ # then you must choose only one of the following (but not both):
109
+ # 1. The trusted_proxies value must match the known proxy IPs. This option is preferable if the IP is static.
110
+ # 2. The trusted_proxy_depth value must be set to the number of known trusted proxies in the chain (see below).
111
+ # This option is preferable if the IPs are ephemeral, but the depth is consistent.
105
112
 
106
113
  # Additionally to make X-Forwarded-For and other headers work better discovering client ip address,
107
114
  # and not the address of a reverse proxy server, you can define trusted proxies
@@ -110,29 +117,48 @@ Castle.configure do |config|
110
117
  # In order to extract the client IP of the X-Forwarded-For header
111
118
  # and not the address of a reverse proxy server, you must define all trusted public proxies
112
119
  # you can achieve this by listing all the proxies ip defined by string or regular expressions
113
- # in trusted_proxies setting
114
- configuration.trusted_proxies = []
120
+ # in the trusted_proxies setting
121
+ config.trusted_proxies = []
115
122
  # or by providing number of trusted proxies used in the chain
116
- configuration.trusted_proxy_depth = 0
123
+ config.trusted_proxy_depth = 0
124
+ # note that you must pick one approach over the other.
117
125
 
118
- # If there is no possibility to define options above and there is no other header which can have client ip
119
- # then you may set trust_proxy_chain = true to trust all of the proxy IP's in X-Forwarded-For
120
- configuration.trust_proxy_chain = false
126
+ # If there is no possibility to define options above and there is no other header that holds the client IP,
127
+ # then you may set trust_proxy_chain = true to trust all of the proxy IPs in X-Forwarded-For
128
+ config.trust_proxy_chain = false
129
+ # *Warning*: this mode is highly promiscuous and could lead to wrongly trusting a spoofed IP if the request passes through a malicious proxy
121
130
 
122
- # *Note: default list of proxies which is always marked as trusted: Castle::Configuration::TRUSTED_PROXIES
131
+ # *Note: the default list of proxies that are always marked as "trusted" can be found in: Castle::Configuration::TRUSTED_PROXIES
123
132
  end
124
133
  ```
125
134
 
135
+ ### Multi-environment configuration
136
+
137
+ It is also possible to define multiple configs within one application.
138
+
139
+ ```ruby
140
+ # Initialize new instance of Castle::Configuration
141
+ config = Castle::Configuration.new.tap do |c|
142
+ # and set any attribute
143
+ c.api_secret = 'YOUR_API_SECRET'
144
+ end
145
+ ```
146
+
147
+ After a successful setup, you can pass the config to any API command as follows:
148
+
149
+ ```ruby
150
+ ::Castle::API::GetDevice.call(device_token: device_token, config: config)
151
+ ```
152
+
126
153
  ## Event Context
127
154
 
128
155
  The client will automatically configure the context for each request.
129
156
 
130
157
  ### Overriding Default Context Properties
131
158
 
132
- If you need to modify the event context properties or if you desire to add additional properties such as user traits to the context, you can pass the properties in as options to the method of interest. An example:
159
+ If you need to modify the event context properties or if you desire to add additional properties such as user traits to the context, you can pass the properties along with the other data. For example:
133
160
  ```ruby
134
- request_context = ::Castle::Client.to_context(request)
135
- track_options = ::Castle::Client.to_options({
161
+ {
136
162
  event: ::Castle::Events::LOGIN_SUCCEEDED,
137
163
  user_id: user.id,
138
164
  properties: {
@@ -140,8 +166,11 @@ track_options = ::Castle::Client.to_options({
140
166
  },
141
167
  user_traits: {
142
168
  key: 'value'
169
+ },
170
+ context: {
171
+ section: 'mobile'
143
172
  }
144
- })
173
+ }
145
174
  ```
146
175
 
147
176
  ## Tracking
@@ -173,9 +202,8 @@ By default Castle sends requests synchronously. To eg. use Sidekiq to send reque
173
202
  class CastleTrackingWorker
174
203
  include Sidekiq::Worker
175
204
 
176
- def perform(context, track_options = {})
177
- client = ::Castle::Client.new(context)
178
- client.track(track_options)
205
+ def perform(payload = {})
206
+ ::Castle::API::Track.call(payload)
179
207
  end
180
208
  end
181
209
  ```
@@ -183,18 +211,20 @@ end
183
211
  #### tracking_controller.rb
184
212
 
185
213
  ```ruby
186
- request_context = ::Castle::Client.to_context(request)
187
- track_options = ::Castle::Client.to_options({
188
- event: ::Castle::Events::LOGIN_SUCCEEDED,
189
- user_id: user.id,
190
- properties: {
191
- key: 'value'
214
+ payload = ::Castle::Payload::Prepare.call(
215
+ {
216
+ event: ::Castle::Events::LOGIN_SUCCEEDED,
217
+ user_id: user.id,
218
+ properties: {
219
+ key: 'value'
220
+ },
221
+ user_traits: {
222
+ key: 'value'
223
+ }
192
224
  },
193
- user_traits: {
194
- key: 'value'
195
- }
196
- })
197
- CastleTrackingWorker.perform_async(request_context, track_options)
225
+ request
226
+ )
227
+ CastleTrackingWorker.perform_async(payload)
198
228
  ```
199
229
 
200
230
  ## Connection reuse
@@ -202,7 +232,7 @@ CastleTrackingWorker.perform_async(request_context, track_options)
202
232
  If you want to reuse the connection to send multiple events:
203
233
 
204
234
  ```ruby
205
- Castle::API::Session.call do |http|
235
+ Castle::Session.call do |http|
206
236
  castle.track(
207
237
  event: ::Castle::Events::LOGOUT_SUCCEEDED,
208
238
  user_id: user2.id
@@ -220,6 +250,40 @@ end
220
250
 
221
251
  List of Recognized Events can be found [here](https://github.com/castle/castle-ruby/tree/master/lib/castle/events.rb) or in the [docs](https://docs.castle.io/api_reference/#list-of-recognized-events)
222
252
 
253
+ ## Device management
254
+
255
+ This SDK allows issuing requests to [Castle's Device Management Endpoints](https://docs.castle.io/device_management_tool/). Use these endpoints for admin-level management of end-user devices (i.e., for an internal dashboard).
256
+
257
+ Fetching device data, approving a device, reporting a device requires a valid `device_token`.
258
+
259
+ ```ruby
260
+ # Get device data
261
+ ::Castle::API::GetDevice.call(device_token: device_token)
262
+ # Approve a device
263
+ ::Castle::API::ApproveDevice.call(device_token: device_token)
264
+ # Report a device
265
+ ::Castle::API::ReportDevice.call(device_token: device_token)
266
+ ```
267
+
268
+ #### castle_device_reporting_worker.rb
269
+
270
+ ```ruby
271
+ class CastleDeviceReportingWorker
272
+ include Sidekiq::Worker
273
+
274
+ def perform(device_token)
275
+ ::Castle::API::ReportDevice.call(device_token: device_token)
276
+ end
277
+ end
278
+ ```
279
+
280
+ Fetching available devices that belong to a given user requires a valid `user_id`.
281
+
282
+ ```ruby
283
+ # Get user's devices data
284
+ ::Castle::API::GetDevicesForUser.call(user_id: user.id)
285
+ ```
286
+
223
287
  ## Impersonation mode
224
288
 
225
289
  https://castle.io/docs/impersonation_mode
@@ -229,6 +293,16 @@ https://castle.io/docs/impersonation_mode
229
293
  `Castle::Error` will be thrown if the Castle API returns a 400 or a 500 level HTTP response.
230
294
  You can also choose to catch a more [finegrained error](https://github.com/castle/castle-ruby/blob/master/lib/castle/errors.rb).
231
295
 
296
+ ## Webhooks
297
+
298
+ Castle uses webhooks to notify about `$incident.confirmed` or `$review.opened` events. Each webhook has `X-Castle-Signature` header that allows verifying webhook's source.
299
+
300
+ ```ruby
301
+ # Verify the webhook, passed as a Request object
302
+ ::Castle::Webhooks::Verify.call(webhook_request)
303
+ # Castle::WebhookVerificationError is raised when the signature is not matching
304
+ ```
305
+
232
306
  ## Documentation
233
307
 
234
308
  [Official Castle docs](https://castle.io/docs)
@@ -9,37 +9,61 @@
9
9
 
10
10
  %w[
11
11
  castle/version
12
+ castle/verdict
12
13
  castle/events
13
14
  castle/errors
14
15
  castle/command
15
- castle/utils
16
- castle/utils/merger
17
- castle/utils/cloner
18
- castle/utils/timestamp
16
+ castle/utils/deep_symbolize_keys
17
+ castle/utils/clean_invalid_chars
18
+ castle/utils/merge
19
+ castle/utils/clone
20
+ castle/utils/get_timestamp
21
+ castle/utils/secure_compare
19
22
  castle/validators/present
20
23
  castle/validators/not_supported
21
- castle/context/merger
22
- castle/context/sanitizer
23
- castle/context/default
24
- castle/commands/identify
24
+ castle/webhooks/verify
25
+ castle/context/merge
26
+ castle/context/sanitize
27
+ castle/context/get_default
28
+ castle/context/prepare
29
+ castle/commands/approve_device
25
30
  castle/commands/authenticate
26
- castle/commands/track
31
+ castle/commands/end_impersonation
32
+ castle/commands/get_device
33
+ castle/commands/get_devices_for_user
34
+ castle/commands/identify
35
+ castle/commands/report_device
27
36
  castle/commands/review
28
- castle/commands/impersonate
37
+ castle/commands/start_impersonation
38
+ castle/commands/track
39
+ castle/api/approve_device
40
+ castle/api/authenticate
41
+ castle/api/end_impersonation
42
+ castle/api/get_device
43
+ castle/api/get_devices_for_user
44
+ castle/api/identify
45
+ castle/api/report_device
46
+ castle/api/review
47
+ castle/api/start_impersonation
48
+ castle/api/track
49
+ castle/payload/prepare
29
50
  castle/configuration
30
- castle/failover_auth_response
51
+ castle/singleton_configuration
52
+ castle/logger
53
+ castle/failover/prepare_response
54
+ castle/failover/strategy
31
55
  castle/client
32
- castle/headers_filter
33
- castle/headers_formatter
56
+ castle/headers/filter
57
+ castle/headers/format
58
+ castle/headers/extract
34
59
  castle/secure_mode
35
- castle/extractors/client_id
36
- castle/extractors/headers
37
- castle/extractors/ip
38
- castle/api/connection
39
- castle/api/response
40
- castle/api/request
41
- castle/api/session
42
- castle/review
60
+ castle/client_id/extract
61
+ castle/ip/extract
62
+ castle/core/get_connection
63
+ castle/core/process_response
64
+ castle/core/send_request
65
+ castle/core/process_webhook
66
+ castle/session
43
67
  castle/api
44
68
  ].each(&method(:require))
45
69
 
@@ -55,7 +79,7 @@ module Castle
55
79
  end
56
80
 
57
81
  def config
58
- Configuration.instance
82
+ SingletonConfiguration.instance
59
83
  end
60
84
 
61
85
  def api_secret=(api_secret)
@@ -20,30 +20,39 @@ module Castle
20
20
  # @param command [String]
21
21
  # @param headers [Hash]
22
22
  # @param http [Net::HTTP]
23
- def request(command, headers = {}, http = nil)
24
- raise Castle::ConfigurationError, 'configuration is not valid' unless Castle.config.valid?
23
+ # @param config [Castle::Configuration, Castle::SingletonConfiguration]
24
+ # @return [Hash]
25
+ def call(command, headers = {}, http = nil, config = Castle.config)
26
+ Castle::Core::ProcessResponse.call(
27
+ send_request(command, headers, http, config)
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ # @param command [String]
34
+ # @param headers [Hash]
35
+ # @param http [Net::HTTP]
36
+ # @param config [Castle::Configuration, Castle::SingletonConfiguration]
37
+ def send_request(command, headers = {}, http = nil, config = Castle.config)
38
+ raise Castle::ConfigurationError, 'configuration is not valid' unless config.valid?
25
39
 
26
40
  begin
27
- Castle::API::Request.call(
41
+ Castle::Core::SendRequest.call(
28
42
  command,
29
- Castle.config.api_secret,
30
43
  headers,
31
- http
44
+ http,
45
+ config
32
46
  )
33
47
  rescue *HANDLED_ERRORS => e
34
48
  # @note We need to initialize the error, as the original error is a cause for this
35
49
  # custom exception. If we would do it the default Ruby way, the original error
36
50
  # would get converted into a string
37
- raise Castle::RequestError.new(e) # rubocop:disable Style/RaiseArgs
51
+ # rubocop:disable Style/RaiseArgs
52
+ raise Castle::RequestError.new(e)
53
+ # rubocop:enable Style/RaiseArgs
38
54
  end
39
55
  end
40
-
41
- # @param command [String]
42
- # @param headers [Hash]
43
- # @param http [Net::HTTP]
44
- def call(command, headers = {}, http = nil)
45
- Castle::API::Response.call(request(command, headers, http))
46
- end
47
56
  end
48
57
  end
49
58
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module API
5
+ # Sends PUT devices/#{device_token}/approve request
6
+ module ApproveDevice
7
+ class << self
8
+ # @param options [Hash]
9
+ # return [Hash]
10
+ def call(options = {})
11
+ options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
12
+ http = options.delete(:http)
13
+ config = options.delete(:config) || Castle.config
14
+
15
+ Castle::API.call(
16
+ Castle::Commands::ApproveDevice.build(options),
17
+ {},
18
+ http,
19
+ config
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module API
5
+ module Authenticate
6
+ class << self
7
+ # @param options [Hash]
8
+ # return [Hash]
9
+ def call(options = {})
10
+ unless options[:no_symbolize]
11
+ options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
12
+ end
13
+ options.delete(:no_symbolize)
14
+ http = options.delete(:http)
15
+ config = options.delete(:config) || Castle.config
16
+
17
+ response = Castle::API.call(
18
+ Castle::Commands::Authenticate.build(options),
19
+ {},
20
+ http,
21
+ config
22
+ )
23
+ response.merge(failover: false, failover_reason: nil)
24
+ rescue Castle::RequestError, Castle::InternalServerError => e
25
+ unless config.failover_strategy == :throw
26
+ return Castle::Failover::PrepareResponse.new(options[:user_id], reason: e.to_s).call
27
+ end
28
+
29
+ raise e
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castle
4
+ module API
5
+ # Sends DELETE impersonate request
6
+ module EndImpersonation
7
+ class << self
8
+ # @param options [Hash]
9
+ def call(options = {})
10
+ unless options[:no_symbolize]
11
+ options = Castle::Utils::DeepSymbolizeKeys.call(options || {})
12
+ end
13
+ options.delete(:no_symbolize)
14
+ http = options.delete(:http)
15
+ config = options.delete(:config) || Castle.config
16
+
17
+ Castle::API.call(
18
+ Castle::Commands::EndImpersonation.build(options),
19
+ {},
20
+ http,
21
+ config
22
+ ).tap do |response|
23
+ raise Castle::ImpersonationFailed unless response[:success]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end