castle-rb 4.1.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +158 -43
  3. data/lib/castle.rb +46 -21
  4. data/lib/castle/api.rb +24 -12
  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 +52 -45
  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 +45 -28
  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/headers/extract.rb +47 -0
  41. data/lib/castle/headers/filter.rb +37 -0
  42. data/lib/castle/headers/format.rb +24 -0
  43. data/lib/castle/ip/extract.rb +83 -0
  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 +41 -23
  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} +9 -34
  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/core/get_connection_spec.rb +59 -0
  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/core/send_request_spec.rb +102 -0
  95. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  96. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +18 -18
  97. data/spec/lib/castle/{headers_filter_spec.rb → headers/filter_spec.rb} +6 -5
  98. data/spec/lib/castle/headers/format_spec.rb +25 -0
  99. data/spec/lib/castle/{extractors/ip_spec.rb → ip/extract_spec.rb} +35 -7
  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/session_spec.rb +88 -0
  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 +133 -56
  114. data/lib/castle/api/request.rb +0 -42
  115. data/lib/castle/api/session.rb +0 -39
  116. data/lib/castle/commands/impersonate.rb +0 -26
  117. data/lib/castle/extractors/headers.rb +0 -45
  118. data/lib/castle/extractors/ip.rb +0 -68
  119. data/lib/castle/failover_auth_response.rb +0 -21
  120. data/lib/castle/headers_filter.rb +0 -35
  121. data/lib/castle/headers_formatter.rb +0 -22
  122. data/lib/castle/review.rb +0 -11
  123. data/lib/castle/utils.rb +0 -55
  124. data/lib/castle/utils/cloner.rb +0 -11
  125. data/lib/castle/utils/timestamp.rb +0 -12
  126. data/spec/lib/castle/api/request_spec.rb +0 -72
  127. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  128. data/spec/lib/castle/utils_spec.rb +0 -156
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0d6aa4996eb091692c0a7dfcf9658b2b26fb011cfa94103604a79e60d401ac0
4
- data.tar.gz: 8e7eb4c21cf3edd7a3845fc946857dd25ad1031d75dfa8cf8cb8b911b928eebe
3
+ metadata.gz: 5e4354fd7a96d78e03da9bbe10015e375af34636a2e5e186c4de361d0851a6ca
4
+ data.tar.gz: 15c6bbf6f34ba315b5f42db490754a92e238f66a006a91e3986b9b1ba540d47a
5
5
  SHA512:
6
- metadata.gz: 9bdf75588225fd282a36d1e37c1ee3eb69ecfab3f3e50a2c27d695f8c7c7ad1a2bccdb8b097dbe9ff361c17481c9449808d9c26adf56924eb249a18ffb2e9a71
7
- data.tar.gz: '089b2507cd099a863b9592c8a194f5971f2126a7ff2892d0f211ace1a9b06261872a9a13d98a72b617f59815d70d87a4c49ca9e348c06031554a3a8cbe905140'
6
+ metadata.gz: 7bb580056e70ed854014054791ef5987874e52b2471838605a90092f70269ba095d40ef5df5310636b0fbd652dfa9460bb95a314e6e2dae5074732672c77122b
7
+ data.tar.gz: 22fb6013ec018e8c983ecf8051041eb62f69f4b54f73528f185f783ab2343e23f5504e3d420143fb3a19f0f49e094a2d56c2fc873411d697b95be645b7cefb47
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ruby SDK for Castle
2
2
 
3
- [![Build Status](https://travis-ci.org/castle/castle-ruby.svg?branch=master)](https://travis-ci.org/castle/castle-ruby)
3
+ [![Build Status](https://circleci.com/gh/castle/castle-ruby.svg?style=shield&branch=master)](https://circleci.com/gh/castle/castle-ruby)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/castle/castle-ruby/badge.svg?branch=coveralls)](https://coveralls.io/github/castle/castle-ruby?branch=coveralls)
5
5
  [![Gem Version](https://badge.fury.io/rb/castle-rb.svg)](https://badge.fury.io/rb/castle-rb)
6
6
 
@@ -62,55 +62,103 @@ 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
67
 
68
- # Whitelisted and Blacklisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
69
- # Whitelisted headers
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)
73
+
74
+ # Allowlisted and Denylisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
75
+ # Allowlisted headers
70
76
  # By default, the SDK sends all HTTP headers, except for Cookie and Authorization.
71
- # If you decide to use a whitelist, the SDK will:
77
+ # If you decide to use a allowlist, the SDK will:
72
78
  # - always send the User-Agent header
73
- # - send scrubbed values of non-whitelisted headers
74
- # - send proper values of whitelisted headers.
79
+ # - send scrubbed values of non-allowlisted headers
80
+ # - send proper values of allowlisted headers.
75
81
  # @example
76
- # config.whitelisted = ['X_HEADER']
82
+ # config.allowlisted = ['X_HEADER']
77
83
  # # will send { 'User-Agent' => 'Chrome', 'X_HEADER' => 'proper value', 'Any-Other-Header' => true }
78
84
  #
79
- # We highly suggest using blacklist instead of whitelist, so that Castle can use as many data points
80
- # as possible to secure your users. If you want to use the whitelist, this is the minimal
85
+ # We highly suggest using denylist instead of allowlist, so that Castle can use as many data points
86
+ # as possible to secure your users. If you want to use the allowlist, this is the minimal
81
87
  # amount of headers we recommend:
82
- config.whitelisted = Castle::Configuration::DEFAULT_WHITELIST
88
+ config.allowlisted = Castle::Configuration::DEFAULT_ALLOWLIST
83
89
 
84
- # Blacklisted headers take precedence over whitelisted elements
85
- # We always blacklist Cookie and Authentication headers. If you use any other headers that
86
- # might contain sensitive information, you should blacklist them.
87
- config.blacklisted = ['HTTP-X-header']
90
+ # Denylisted headers take precedence over allowlisted elements
91
+ # We always denylist Cookie and Authentication headers. If you use any other headers that
92
+ # might contain sensitive information, you should denylist them.
93
+ config.denylisted = ['HTTP-X-header']
88
94
 
89
95
  # Castle needs the original IP of the client, not the IP of your proxy or load balancer.
90
- # we try to fetch proper ip based on X-Forwarded-For, X-Client-Id or Remote-Addr headers in that order
91
- # but sometimes proper ip may be stored in different header or order could be different.
92
- # SDK can extract ip automatically for you, but you must configure which ip_headers you would like to use
93
- configuration.ip_headers = []
94
-
95
- # Additionally to make X-Forwarded-For or X-Client-Id work better discovering client ip address,
96
+ # The SDK will only trust the proxy chain as defined in the configuration.
97
+ # We try to fetch the client IP based on X-Forwarded-For or Remote-Addr headers in that order,
98
+ # but sometimes the client IP may be stored in a different header or order.
99
+ # The SDK can be configured to look for the client IP address in headers that you specify.
100
+
101
+ # Sometimes, Cloud providers do not use consistent IP addresses to proxy requests.
102
+ # In this case, the client IP is usually preserved in a custom header. Example:
103
+ # Cloudflare preserves the client request in the 'Cf-Connecting-Ip' header.
104
+ # It would be used like so: config.ip_headers=['Cf-Connecting-Ip']
105
+ config.ip_headers = []
106
+
107
+ # If the specified header or X-Forwarded-For default contains a proxy chain with public IP addresses,
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.
112
+
113
+ # Additionally to make X-Forwarded-For and other headers work better discovering client ip address,
96
114
  # and not the address of a reverse proxy server, you can define trusted proxies
97
115
  # which will help to fetch proper ip from those headers
98
- configuration.trusted_proxies = []
99
- # *Note: proxies list can be provided as an array of regular expressions
100
- # *Note: default always marked as trusty list is here: Castle::Configuration::TRUSTED_PROXIES
116
+
117
+ # In order to extract the client IP of the X-Forwarded-For header
118
+ # and not the address of a reverse proxy server, you must define all trusted public proxies
119
+ # you can achieve this by listing all the proxies ip defined by string or regular expressions
120
+ # in the trusted_proxies setting
121
+ config.trusted_proxies = []
122
+ # or by providing number of trusted proxies used in the chain
123
+ config.trusted_proxy_depth = 0
124
+ # note that you must pick one approach over the other.
125
+
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
130
+
131
+ # *Note: the default list of proxies that are always marked as "trusted" can be found in: Castle::Configuration::TRUSTED_PROXIES
101
132
  end
102
133
  ```
103
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
+
104
153
  ## Event Context
105
154
 
106
155
  The client will automatically configure the context for each request.
107
156
 
108
157
  ### Overriding Default Context Properties
109
158
 
110
- 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:
111
160
  ```ruby
112
- request_context = ::Castle::Client.to_context(request)
113
- track_options = ::Castle::Client.to_options({
161
+ {
114
162
  event: ::Castle::Events::LOGIN_SUCCEEDED,
115
163
  user_id: user.id,
116
164
  properties: {
@@ -118,8 +166,11 @@ track_options = ::Castle::Client.to_options({
118
166
  },
119
167
  user_traits: {
120
168
  key: 'value'
169
+ },
170
+ context: {
171
+ section: 'mobile'
121
172
  }
122
- })
173
+ }
123
174
  ```
124
175
 
125
176
  ## Tracking
@@ -151,9 +202,8 @@ By default Castle sends requests synchronously. To eg. use Sidekiq to send reque
151
202
  class CastleTrackingWorker
152
203
  include Sidekiq::Worker
153
204
 
154
- def perform(context, track_options = {})
155
- client = ::Castle::Client.new(context)
156
- client.track(track_options)
205
+ def perform(payload = {})
206
+ ::Castle::API::Track.call(payload)
157
207
  end
158
208
  end
159
209
  ```
@@ -161,24 +211,79 @@ end
161
211
  #### tracking_controller.rb
162
212
 
163
213
  ```ruby
164
- request_context = ::Castle::Client.to_context(request)
165
- track_options = ::Castle::Client.to_options({
166
- event: ::Castle::Events::LOGIN_SUCCEEDED,
167
- user_id: user.id,
168
- properties: {
169
- 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
+ }
170
224
  },
171
- user_traits: {
172
- key: 'value'
173
- }
174
- })
175
- CastleTrackingWorker.perform_async(request_context, track_options)
225
+ request
226
+ )
227
+ CastleTrackingWorker.perform_async(payload)
228
+ ```
229
+
230
+ ## Connection reuse
231
+
232
+ If you want to reuse the connection to send multiple events:
233
+
234
+ ```ruby
235
+ Castle::Session.call do |http|
236
+ castle.track(
237
+ event: ::Castle::Events::LOGOUT_SUCCEEDED,
238
+ user_id: user2.id
239
+ http: http
240
+ )
241
+ castle.track(
242
+ event: ::Castle::Events::LOGIN_SUCCEEDED,
243
+ user_id: user1.id
244
+ http: http
245
+ )
246
+ end
176
247
  ```
177
248
 
178
249
  ## Events
179
250
 
180
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)
181
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
+
182
287
  ## Impersonation mode
183
288
 
184
289
  https://castle.io/docs/impersonation_mode
@@ -188,6 +293,16 @@ https://castle.io/docs/impersonation_mode
188
293
  `Castle::Error` will be thrown if the Castle API returns a 400 or a 500 level HTTP response.
189
294
  You can also choose to catch a more [finegrained error](https://github.com/castle/castle-ruby/blob/master/lib/castle/errors.rb).
190
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
+
191
306
  ## Documentation
192
307
 
193
308
  [Official Castle docs](https://castle.io/docs)
@@ -9,36 +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/response
39
- castle/api/request
40
- castle/api/session
41
- 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
42
67
  castle/api
43
68
  ].each(&method(:require))
44
69
 
@@ -54,7 +79,7 @@ module Castle
54
79
  end
55
80
 
56
81
  def config
57
- Configuration.instance
82
+ SingletonConfiguration.instance
58
83
  end
59
84
 
60
85
  def api_secret=(api_secret)
@@ -19,28 +19,40 @@ module Castle
19
19
  class << self
20
20
  # @param command [String]
21
21
  # @param headers [Hash]
22
- def request(command, headers = {})
23
- raise Castle::ConfigurationError, 'configuration is not valid' unless Castle.config.valid?
22
+ # @param http [Net::HTTP]
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?
24
39
 
25
40
  begin
26
- Castle::API::Request.call(
41
+ Castle::Core::SendRequest.call(
27
42
  command,
28
- Castle.config.api_secret,
29
- headers
43
+ headers,
44
+ http,
45
+ config
30
46
  )
31
47
  rescue *HANDLED_ERRORS => e
32
48
  # @note We need to initialize the error, as the original error is a cause for this
33
49
  # custom exception. If we would do it the default Ruby way, the original error
34
50
  # would get converted into a string
35
- 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
36
54
  end
37
55
  end
38
-
39
- # @param command [String]
40
- # @param headers [Hash]
41
- def call(command, headers = {})
42
- Castle::API::Response.call(request(command, headers))
43
- end
44
56
  end
45
57
  end
46
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