castle-rb 4.3.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -49
  3. data/lib/castle.rb +49 -28
  4. data/lib/castle/api.rb +21 -14
  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 +78 -51
  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 +49 -32
  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 +18 -0
  40. data/lib/castle/failover/strategy.rb +23 -0
  41. data/lib/castle/headers/extract.rb +47 -0
  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 +108 -93
  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/start_impersonation_spec.rb +79 -0
  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/core/send_request_spec.rb +77 -0
  100. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  101. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +18 -20
  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 +152 -0
  120. data/spec/support/shared_examples/configuration.rb +101 -0
  121. metadata +147 -67
  122. data/lib/castle/api/request.rb +0 -42
  123. data/lib/castle/api/session.rb +0 -39
  124. data/lib/castle/commands/identify.rb +0 -23
  125. data/lib/castle/commands/impersonate.rb +0 -26
  126. data/lib/castle/commands/review.rb +0 -14
  127. data/lib/castle/events.rb +0 -49
  128. data/lib/castle/extractors/headers.rb +0 -45
  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/request_spec.rb +0 -72
  137. data/spec/lib/castle/api/session_spec.rb +0 -47
  138. data/spec/lib/castle/commands/identify_spec.rb +0 -88
  139. data/spec/lib/castle/commands/impersonate_spec.rb +0 -107
  140. data/spec/lib/castle/events_spec.rb +0 -5
  141. data/spec/lib/castle/headers_filter_spec.rb +0 -38
  142. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  143. data/spec/lib/castle/review_spec.rb +0 -19
  144. data/spec/lib/castle/utils/merger_spec.rb +0 -13
  145. data/spec/lib/castle/utils_spec.rb +0 -156
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0dd544ffa6fc2660fc67c058011df5b9d83a8078b48506ab611809166960f501
4
- data.tar.gz: 1720630a7f1925ba1142208114198cf176a8a3fec5c04be480aa4d2384e03de2
3
+ metadata.gz: 1194491c10ae0be7d9dbb1a0ed8c2d86765c84ff21750db3c4a991bacde13155
4
+ data.tar.gz: efbe4311bab7d72428fd18e648d25c27fea5a528ea422ae9766a223500523404
5
5
  SHA512:
6
- metadata.gz: 71320c8ff0a5dd2a137723efc76c5530449bdf4635eed38f4c5fcad8bcb9253c5b2557b9113071cd606528eb58d3d66921fa1a728e6ea123d65ab2173e71ad59
7
- data.tar.gz: d2f57e05bd976a294385808757b148248f01b2319cd99e88277bc18e104d4a2fabbcd2d1aa55057d7fc5a12e4ab7e3ec66086502c74ba6d8e5f6b8e51acfba01
6
+ metadata.gz: 3cea34bb26091eea34a91e9ec240863cca2c199926f40af0312ca2548dbd3ed2848a8812ae4d080641a275d541cd9d1a150ee4b9efb0697407e11f254c77412c
7
+ data.tar.gz: 61ce4a2b5041efd6518367ae18eb0c064b7cfb928c2458ea125183467522d10c4f45f4b85012c586125da221f03d9c8ac5cc338ded7d00bf839f6880e6c522cc
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,40 +62,53 @@ 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
96
  # The SDK will only trust the proxy chain as defined in the configuration.
91
97
  # We try to fetch the client IP based on X-Forwarded-For or Remote-Addr headers in that order,
92
98
  # but sometimes the client IP may be stored in a different header or order.
93
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
+
94
107
  # If the specified header or X-Forwarded-For default contains a proxy chain with public IP addresses,
95
- # then one of the following must be set
96
- # 1. The trusted_proxies value must match the known proxy IP's
97
- # 2. The trusted_proxy_depth value must be set to the number of known trusted proxies in the chain (see below)
98
- configuration.ip_headers = []
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.
99
112
 
100
113
  # Additionally to make X-Forwarded-For and other headers work better discovering client ip address,
101
114
  # and not the address of a reverse proxy server, you can define trusted proxies
@@ -104,38 +117,60 @@ Castle.configure do |config|
104
117
  # In order to extract the client IP of the X-Forwarded-For header
105
118
  # and not the address of a reverse proxy server, you must define all trusted public proxies
106
119
  # you can achieve this by listing all the proxies ip defined by string or regular expressions
107
- # in trusted_proxies setting
108
- configuration.trusted_proxies = []
120
+ # in the trusted_proxies setting
121
+ config.trusted_proxies = []
109
122
  # or by providing number of trusted proxies used in the chain
110
- configuration.trusted_proxy_depth = 0
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
132
+ end
133
+ ```
134
+
135
+ ### Multi-environment configuration
111
136
 
112
- # If there is no possibility to define options above and there is no other header which can have client ip
113
- # then you may set trust_proxy_chain = true to trust all of the proxy IP's in X-Forwarded-For
114
- configuration.trust_proxy_chain = false
137
+ It is also possible to define multiple configs within one application.
115
138
 
116
- # *Note: default list of proxies which is always marked as trusted: Castle::Configuration::TRUSTED_PROXIES
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'
117
144
  end
118
145
  ```
119
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
+
120
153
  ## Event Context
121
154
 
122
155
  The client will automatically configure the context for each request.
123
156
 
124
157
  ### Overriding Default Context Properties
125
158
 
126
- 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:
127
160
  ```ruby
128
- request_context = ::Castle::Client.to_context(request)
129
- track_options = ::Castle::Client.to_options({
130
- event: ::Castle::Events::LOGIN_SUCCEEDED,
161
+ {
162
+ event: '$login.succeeded',
131
163
  user_id: user.id,
132
164
  properties: {
133
165
  key: 'value'
134
166
  },
135
167
  user_traits: {
136
168
  key: 'value'
169
+ },
170
+ context: {
171
+ section: 'mobile'
137
172
  }
138
- })
173
+ }
139
174
  ```
140
175
 
141
176
  ## Tracking
@@ -145,7 +180,7 @@ Here is a simple example of a track event.
145
180
  ```ruby
146
181
  begin
147
182
  castle.track(
148
- event: ::Castle::Events::LOGIN_SUCCEEDED,
183
+ event: '$login.succeeded',
149
184
  user_id: user.id
150
185
  )
151
186
  rescue Castle::Error => e
@@ -167,9 +202,8 @@ By default Castle sends requests synchronously. To eg. use Sidekiq to send reque
167
202
  class CastleTrackingWorker
168
203
  include Sidekiq::Worker
169
204
 
170
- def perform(context, track_options = {})
171
- client = ::Castle::Client.new(context)
172
- client.track(track_options)
205
+ def perform(payload = {})
206
+ ::Castle::API::Track.call(payload)
173
207
  end
174
208
  end
175
209
  ```
@@ -177,23 +211,78 @@ end
177
211
  #### tracking_controller.rb
178
212
 
179
213
  ```ruby
180
- request_context = ::Castle::Client.to_context(request)
181
- track_options = ::Castle::Client.to_options({
182
- event: ::Castle::Events::LOGIN_SUCCEEDED,
183
- user_id: user.id,
184
- properties: {
185
- key: 'value'
214
+ payload = ::Castle::Payload::Prepare.call(
215
+ {
216
+ event: '$login.succeeded',
217
+ user_id: user.id,
218
+ properties: {
219
+ key: 'value'
220
+ },
221
+ user_traits: {
222
+ key: 'value'
223
+ }
186
224
  },
187
- user_traits: {
188
- key: 'value'
189
- }
190
- })
191
- 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: '$logout.succeeded',
238
+ user_id: user2.id
239
+ http: http
240
+ )
241
+ castle.track(
242
+ event: '$login.succeeded',
243
+ user_id: user1.id
244
+ http: http
245
+ )
246
+ end
192
247
  ```
193
248
 
194
249
  ## Events
195
250
 
196
- 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)
251
+ List of Recognized Events can be found in the [docs](https://docs.castle.io/v1/reference/events/)
252
+
253
+ ## Device management
254
+
255
+ This SDK allows issuing requests to [Castle's Device Management Endpoints](https://docs.castle.io/v1/reference/api-reference/#devices). 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
+ ```
197
286
 
198
287
  ## Impersonation mode
199
288
 
@@ -204,6 +293,16 @@ https://castle.io/docs/impersonation_mode
204
293
  `Castle::Error` will be thrown if the Castle API returns a 400 or a 500 level HTTP response.
205
294
  You can also choose to catch a more [finegrained error](https://github.com/castle/castle-ruby/blob/master/lib/castle/errors.rb).
206
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
+
207
306
  ## Documentation
208
307
 
209
- [Official Castle docs](https://castle.io/docs)
308
+ [Official Castle docs](https://docs.castle.io/)
data/lib/castle.rb CHANGED
@@ -1,44 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- %w[
4
- openssl
5
- net/http
6
- json
7
- time
8
- ].each(&method(:require))
3
+ %w[openssl net/http json time].each(&method(:require))
9
4
 
10
5
  %w[
11
6
  castle/version
12
- castle/events
7
+ castle/verdict
13
8
  castle/errors
14
9
  castle/command
15
- castle/utils
16
- castle/utils/merger
17
- castle/utils/cloner
18
- castle/utils/timestamp
10
+ castle/utils/deep_symbolize_keys
11
+ castle/utils/clean_invalid_chars
12
+ castle/utils/merge
13
+ castle/utils/clone
14
+ castle/utils/get_timestamp
15
+ castle/utils/secure_compare
19
16
  castle/validators/present
20
17
  castle/validators/not_supported
21
- castle/context/merger
22
- castle/context/sanitizer
23
- castle/context/default
24
- castle/commands/identify
18
+ castle/webhooks/verify
19
+ castle/context/merge
20
+ castle/context/sanitize
21
+ castle/context/get_default
22
+ castle/context/prepare
23
+ castle/commands/approve_device
25
24
  castle/commands/authenticate
25
+ castle/commands/end_impersonation
26
+ castle/commands/filter
27
+ castle/commands/get_device
28
+ castle/commands/get_devices_for_user
29
+ castle/commands/log
30
+ castle/commands/report_device
31
+ castle/commands/risk
32
+ castle/commands/start_impersonation
26
33
  castle/commands/track
27
- castle/commands/review
28
- castle/commands/impersonate
34
+ castle/api/approve_device
35
+ castle/api/authenticate
36
+ castle/api/end_impersonation
37
+ castle/api/filter
38
+ castle/api/get_device
39
+ castle/api/get_devices_for_user
40
+ castle/api/log
41
+ castle/api/report_device
42
+ castle/api/risk
43
+ castle/api/start_impersonation
44
+ castle/api/track
45
+ castle/payload/prepare
29
46
  castle/configuration
30
- castle/failover_auth_response
47
+ castle/singleton_configuration
48
+ castle/logger
49
+ castle/failover/prepare_response
50
+ castle/failover/strategy
31
51
  castle/client
32
- castle/headers_filter
33
- castle/headers_formatter
52
+ castle/headers/filter
53
+ castle/headers/format
54
+ castle/headers/extract
34
55
  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
56
+ castle/client_id/extract
57
+ castle/ips/extract
58
+ castle/core/get_connection
59
+ castle/core/process_response
60
+ castle/core/send_request
61
+ castle/core/process_webhook
62
+ castle/session
42
63
  castle/api
43
64
  ].each(&method(:require))
44
65
 
@@ -54,7 +75,7 @@ module Castle
54
75
  end
55
76
 
56
77
  def config
57
- Configuration.instance
78
+ SingletonConfiguration.instance
58
79
  end
59
80
 
60
81
  def api_secret=(api_secret)
data/lib/castle/api.rb CHANGED
@@ -19,28 +19,35 @@ 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, nil]
24
+ # @return [Hash]
25
+ def call(command, headers = {}, http = nil, config = nil)
26
+ Castle::Core::ProcessResponse.call(send_request(command, headers, http, config), config)
27
+ end
28
+
29
+ private
30
+
31
+ # @param command [String]
32
+ # @param headers [Hash]
33
+ # @param http [Net::HTTP]
34
+ # @param config [Castle::Configuration, Castle::SingletonConfiguration]
35
+ def send_request(command, headers = {}, http = nil, config = nil)
36
+ config ||= Castle.config
37
+
38
+ raise Castle::ConfigurationError, 'configuration is not valid' unless config.valid?
24
39
 
25
40
  begin
26
- Castle::API::Request.call(
27
- command,
28
- Castle.config.api_secret,
29
- headers
30
- )
41
+ Castle::Core::SendRequest.call(command, headers, http, config)
31
42
  rescue *HANDLED_ERRORS => e
32
43
  # @note We need to initialize the error, as the original error is a cause for this
33
44
  # custom exception. If we would do it the default Ruby way, the original error
34
45
  # would get converted into a string
35
- raise Castle::RequestError.new(e) # rubocop:disable Style/RaiseArgs
46
+ # rubocop:disable Style/RaiseArgs
47
+ raise Castle::RequestError.new(e)
48
+ # rubocop:enable Style/RaiseArgs
36
49
  end
37
50
  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
51
  end
45
52
  end
46
53
  end
@@ -0,0 +1,20 @@
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(Castle::Commands::ApproveDevice.build(options), {}, http, config)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end