castle-rb 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +107 -33
- data/lib/castle.rb +46 -22
- data/lib/castle/api.rb +22 -13
- data/lib/castle/api/approve_device.rb +25 -0
- data/lib/castle/api/authenticate.rb +34 -0
- data/lib/castle/api/end_impersonation.rb +29 -0
- data/lib/castle/api/get_device.rb +25 -0
- data/lib/castle/api/get_devices_for_user.rb +25 -0
- data/lib/castle/api/identify.rb +26 -0
- data/lib/castle/api/report_device.rb +25 -0
- data/lib/castle/api/review.rb +24 -0
- data/lib/castle/api/start_impersonation.rb +29 -0
- data/lib/castle/api/track.rb +26 -0
- data/lib/castle/client.rb +48 -62
- data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
- data/lib/castle/commands/approve_device.rb +21 -0
- data/lib/castle/commands/authenticate.rb +13 -13
- data/lib/castle/commands/end_impersonation.rb +25 -0
- data/lib/castle/commands/get_device.rb +21 -0
- data/lib/castle/commands/get_devices_for_user.rb +21 -0
- data/lib/castle/commands/identify.rb +12 -13
- data/lib/castle/commands/report_device.rb +21 -0
- data/lib/castle/commands/review.rb +6 -3
- data/lib/castle/commands/start_impersonation.rb +25 -0
- data/lib/castle/commands/track.rb +12 -13
- data/lib/castle/configuration.rb +17 -19
- data/lib/castle/context/{default.rb → get_default.rb} +5 -6
- data/lib/castle/context/{merger.rb → merge.rb} +3 -3
- data/lib/castle/context/prepare.rb +18 -0
- data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
- data/lib/castle/core/get_connection.rb +25 -0
- data/lib/castle/{api/response.rb → core/process_response.rb} +4 -2
- data/lib/castle/core/process_webhook.rb +20 -0
- data/lib/castle/core/send_request.rb +50 -0
- data/lib/castle/errors.rb +2 -0
- data/lib/castle/events.rb +1 -1
- data/lib/castle/failover/prepare_response.rb +23 -0
- data/lib/castle/failover/strategy.rb +20 -0
- data/lib/castle/{extractors/headers.rb → headers/extract.rb} +8 -6
- data/lib/castle/headers/filter.rb +37 -0
- data/lib/castle/headers/format.rb +24 -0
- data/lib/castle/{extractors/ip.rb → ip/extract.rb} +8 -7
- data/lib/castle/logger.rb +19 -0
- data/lib/castle/payload/prepare.rb +27 -0
- data/lib/castle/secure_mode.rb +6 -2
- data/lib/castle/session.rb +18 -0
- data/lib/castle/singleton_configuration.rb +9 -0
- data/lib/castle/utils/clean_invalid_chars.rb +24 -0
- data/lib/castle/utils/clone.rb +15 -0
- data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
- data/lib/castle/utils/get_timestamp.rb +15 -0
- data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
- data/lib/castle/utils/secure_compare.rb +22 -0
- data/lib/castle/validators/not_supported.rb +1 -0
- data/lib/castle/validators/present.rb +1 -0
- data/lib/castle/verdict.rb +13 -0
- data/lib/castle/version.rb +1 -1
- data/lib/castle/webhooks/verify.rb +43 -0
- data/spec/integration/rails/rails_spec.rb +33 -7
- data/spec/integration/rails/support/application.rb +3 -1
- data/spec/integration/rails/support/home_controller.rb +47 -5
- data/spec/lib/castle/api/approve_device_spec.rb +21 -0
- data/spec/lib/castle/api/authenticate_spec.rb +140 -0
- data/spec/lib/castle/api/end_impersonation_spec.rb +59 -0
- data/spec/lib/castle/api/get_device_spec.rb +19 -0
- data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
- data/spec/lib/castle/api/identify_spec.rb +68 -0
- data/spec/lib/castle/api/report_device_spec.rb +21 -0
- data/spec/lib/castle/{review_spec.rb → api/review_spec.rb} +3 -3
- data/spec/lib/castle/api/start_impersonation_spec.rb +59 -0
- data/spec/lib/castle/api/track_spec.rb +68 -0
- data/spec/lib/castle/api_spec.rb +16 -1
- data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +2 -2
- data/spec/lib/castle/client_spec.rb +39 -21
- data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
- data/spec/lib/castle/commands/authenticate_spec.rb +7 -16
- data/spec/lib/castle/commands/end_impersonation_spec.rb +82 -0
- data/spec/lib/castle/commands/get_device_spec.rb +24 -0
- data/spec/lib/castle/commands/get_devices_for_user_spec.rb +24 -0
- data/spec/lib/castle/commands/identify_spec.rb +5 -16
- data/spec/lib/castle/commands/report_device_spec.rb +24 -0
- data/spec/lib/castle/commands/review_spec.rb +1 -1
- data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +7 -32
- data/spec/lib/castle/commands/track_spec.rb +5 -16
- data/spec/lib/castle/configuration_spec.rb +9 -138
- data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +1 -2
- data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
- data/spec/lib/castle/context/prepare_spec.rb +44 -0
- data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
- data/spec/lib/castle/{api/connection_spec.rb → core/get_connection_spec.rb} +3 -3
- data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +56 -1
- data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
- data/spec/lib/castle/{api/request_spec.rb → core/send_request_spec.rb} +20 -16
- data/spec/lib/castle/failover/strategy_spec.rb +12 -0
- data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +7 -7
- data/spec/lib/castle/{headers_filter_spec.rb → headers/filter_spec.rb} +3 -3
- data/spec/lib/castle/headers/format_spec.rb +25 -0
- data/spec/lib/castle/{extractors/ip_spec.rb → ip/extract_spec.rb} +1 -1
- data/spec/lib/castle/logger_spec.rb +42 -0
- data/spec/lib/castle/payload/prepare_spec.rb +54 -0
- data/spec/lib/castle/{api/session_spec.rb → session_spec.rb} +6 -4
- data/spec/lib/castle/singleton_configuration_spec.rb +18 -0
- data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
- data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
- data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
- data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
- data/spec/lib/castle/utils/{merger_spec.rb → merge_spec.rb} +3 -3
- data/spec/lib/castle/verdict_spec.rb +9 -0
- data/spec/lib/castle/webhooks/verify_spec.rb +69 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/shared_examples/configuration.rb +129 -0
- metadata +129 -57
- data/lib/castle/api/connection.rb +0 -24
- data/lib/castle/api/request.rb +0 -42
- data/lib/castle/api/session.rb +0 -20
- data/lib/castle/commands/impersonate.rb +0 -26
- data/lib/castle/failover_auth_response.rb +0 -21
- data/lib/castle/headers_filter.rb +0 -35
- data/lib/castle/headers_formatter.rb +0 -22
- data/lib/castle/review.rb +0 -11
- data/lib/castle/utils.rb +0 -55
- data/lib/castle/utils/cloner.rb +0 -11
- data/lib/castle/utils/timestamp.rb +0 -12
- data/spec/lib/castle/headers_formatter_spec.rb +0 -25
- data/spec/lib/castle/utils_spec.rb +0 -156
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# Generates the payload for the GET devices/#{device_token} request
|
6
|
+
class GetDevice
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[device_token])
|
12
|
+
Castle::Command.new(
|
13
|
+
"devices/#{options[:device_token]}",
|
14
|
+
nil,
|
15
|
+
:get
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# Generates the payload for the GET users/#{user_id}/devices request
|
6
|
+
class GetDevicesForUser
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[user_id])
|
12
|
+
Castle::Command.new(
|
13
|
+
"users/#{options[:user_id]}/devices",
|
14
|
+
nil,
|
15
|
+
:get
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,20 +3,19 @@
|
|
3
3
|
module Castle
|
4
4
|
module Commands
|
5
5
|
class Identify
|
6
|
-
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
context = Castle::Context::Merger.call(@context, options[:context])
|
13
|
-
context = Castle::Context::Sanitizer.call(context)
|
6
|
+
class << self
|
7
|
+
# @param options [Hash]
|
8
|
+
# @return [Castle::Command]
|
9
|
+
def build(options = {})
|
10
|
+
Castle::Validators::NotSupported.call(options, %i[properties])
|
11
|
+
context = Castle::Context::Sanitize.call(options[:context])
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
Castle::Command.new(
|
14
|
+
'identify',
|
15
|
+
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
16
|
+
:post
|
17
|
+
)
|
18
|
+
end
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# Generates the payload for the PUT devices/#{device_token}/report request
|
6
|
+
class ReportDevice
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[device_token])
|
12
|
+
Castle::Command.new(
|
13
|
+
"devices/#{options[:device_token]}/report",
|
14
|
+
nil,
|
15
|
+
:put
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
module Commands
|
5
|
+
# Generates the payload for the GET reviews/#{review_id} request
|
5
6
|
class Review
|
6
7
|
class << self
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[review_id])
|
12
|
+
Castle::Command.new("reviews/#{options[:review_id]}", nil, :get)
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# builder for impersonate command
|
6
|
+
class StartImpersonation
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[user_id])
|
12
|
+
context = Castle::Context::Sanitize.call(options[:context])
|
13
|
+
|
14
|
+
Castle::Validators::Present.call(context, %i[user_agent ip])
|
15
|
+
|
16
|
+
Castle::Command.new(
|
17
|
+
'impersonate',
|
18
|
+
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
19
|
+
:post
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,20 +3,19 @@
|
|
3
3
|
module Castle
|
4
4
|
module Commands
|
5
5
|
class Track
|
6
|
-
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
context = Castle::Context::Merger.call(@context, options[:context])
|
13
|
-
context = Castle::Context::Sanitizer.call(context)
|
6
|
+
class << self
|
7
|
+
# @param options [Hash]
|
8
|
+
# @return [Castle::Command]
|
9
|
+
def build(options = {})
|
10
|
+
Castle::Validators::Present.call(options, %i[event])
|
11
|
+
context = Castle::Context::Sanitize.call(options[:context])
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
Castle::Command.new(
|
14
|
+
'track',
|
15
|
+
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
16
|
+
:post
|
17
|
+
)
|
18
|
+
end
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
data/lib/castle/configuration.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'singleton'
|
4
3
|
require 'uri'
|
5
4
|
|
6
5
|
module Castle
|
7
6
|
# manages configuration variables
|
8
7
|
class Configuration
|
9
|
-
include Singleton
|
10
|
-
|
11
8
|
# API endpoint
|
12
|
-
|
13
|
-
|
14
|
-
REQUEST_TIMEOUT = 500 # in milliseconds
|
15
|
-
FAILOVER_STRATEGIES = %i[allow deny challenge throw].freeze
|
9
|
+
BASE_URL = 'https://api.castle.io/v1'
|
10
|
+
REQUEST_TIMEOUT = 1000 # in milliseconds
|
16
11
|
# regexp of trusted proxies which is always appended to the trusted proxy list
|
17
12
|
TRUSTED_PROXIES = [/
|
18
13
|
\A127\.0\.0\.1\Z|
|
@@ -51,19 +46,19 @@ module Castle
|
|
51
46
|
X-Requested-With
|
52
47
|
].freeze
|
53
48
|
|
54
|
-
attr_accessor :request_timeout, :trust_proxy_chain
|
49
|
+
attr_accessor :request_timeout, :trust_proxy_chain, :logger
|
55
50
|
attr_reader :api_secret, :allowlisted, :denylisted, :failover_strategy, :ip_headers,
|
56
|
-
:trusted_proxies, :trusted_proxy_depth, :
|
51
|
+
:trusted_proxies, :trusted_proxy_depth, :base_url
|
57
52
|
|
58
53
|
def initialize
|
59
|
-
@
|
54
|
+
@header_format = Castle::Headers::Format
|
60
55
|
@request_timeout = REQUEST_TIMEOUT
|
61
56
|
reset
|
62
57
|
end
|
63
58
|
|
64
59
|
def reset
|
65
|
-
self.failover_strategy =
|
66
|
-
self.
|
60
|
+
self.failover_strategy = Castle::Failover::Strategy::ALLOW
|
61
|
+
self.base_url = BASE_URL
|
67
62
|
self.allowlisted = [].freeze
|
68
63
|
self.denylisted = [].freeze
|
69
64
|
self.api_secret = ENV.fetch('CASTLE_API_SECRET', '')
|
@@ -71,10 +66,11 @@ module Castle
|
|
71
66
|
self.trusted_proxies = [].freeze
|
72
67
|
self.trust_proxy_chain = false
|
73
68
|
self.trusted_proxy_depth = nil
|
69
|
+
self.logger = nil
|
74
70
|
end
|
75
71
|
|
76
|
-
def
|
77
|
-
@
|
72
|
+
def base_url=(value)
|
73
|
+
@base_url = URI(value)
|
78
74
|
end
|
79
75
|
|
80
76
|
def api_secret=(value)
|
@@ -82,11 +78,11 @@ module Castle
|
|
82
78
|
end
|
83
79
|
|
84
80
|
def allowlisted=(value)
|
85
|
-
@allowlisted = (value ? value.map { |header| @
|
81
|
+
@allowlisted = (value ? value.map { |header| @header_format.call(header) } : []).freeze
|
86
82
|
end
|
87
83
|
|
88
84
|
def denylisted=(value)
|
89
|
-
@denylisted = (value ? value.map { |header| @
|
85
|
+
@denylisted = (value ? value.map { |header| @header_format.call(header) } : []).freeze
|
90
86
|
end
|
91
87
|
|
92
88
|
# sets ip headers
|
@@ -94,7 +90,7 @@ module Castle
|
|
94
90
|
def ip_headers=(value)
|
95
91
|
raise Castle::ConfigurationError, 'ip headers must be an Array' unless value.is_a?(Array)
|
96
92
|
|
97
|
-
@ip_headers = value.map { |header| @
|
93
|
+
@ip_headers = value.map { |header| @header_format.call(header) }.freeze
|
98
94
|
end
|
99
95
|
|
100
96
|
# sets trusted proxies
|
@@ -111,11 +107,13 @@ module Castle
|
|
111
107
|
end
|
112
108
|
|
113
109
|
def valid?
|
114
|
-
!api_secret.to_s.empty? && !
|
110
|
+
!api_secret.to_s.empty? && !base_url.host.to_s.empty? && !base_url.port.to_s.empty?
|
115
111
|
end
|
116
112
|
|
117
113
|
def failover_strategy=(value)
|
118
|
-
@failover_strategy =
|
114
|
+
@failover_strategy = Castle::Failover::STRATEGIES.detect do |strategy|
|
115
|
+
strategy == value.to_sym
|
116
|
+
end
|
119
117
|
raise Castle::ConfigurationError, 'unrecognized failover strategy' if @failover_strategy.nil?
|
120
118
|
end
|
121
119
|
|
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
module Context
|
5
|
-
class
|
5
|
+
class GetDefault
|
6
6
|
def initialize(request, cookies = nil)
|
7
|
-
@pre_headers =
|
7
|
+
@pre_headers = Castle::Headers::Filter.new(request).call
|
8
8
|
@cookies = cookies || request.cookies
|
9
9
|
@request = request
|
10
10
|
end
|
@@ -13,7 +13,6 @@ module Castle
|
|
13
13
|
{
|
14
14
|
client_id: client_id,
|
15
15
|
active: true,
|
16
|
-
origin: 'web',
|
17
16
|
headers: headers,
|
18
17
|
ip: ip,
|
19
18
|
library: {
|
@@ -40,18 +39,18 @@ module Castle
|
|
40
39
|
|
41
40
|
# @return [String]
|
42
41
|
def ip
|
43
|
-
|
42
|
+
Castle::IP::Extract.new(@pre_headers).call
|
44
43
|
end
|
45
44
|
|
46
45
|
# @return [String]
|
47
46
|
def client_id
|
48
|
-
|
47
|
+
Castle::ClientId::Extract.new(@pre_headers, @cookies).call
|
49
48
|
end
|
50
49
|
|
51
50
|
# formatted and filtered headers
|
52
51
|
# @return [Hash]
|
53
52
|
def headers
|
54
|
-
|
53
|
+
Castle::Headers::Extract.new(@pre_headers).call
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
module Context
|
5
|
-
class
|
5
|
+
class Merge
|
6
6
|
class << self
|
7
7
|
def call(initial_context, request_context)
|
8
|
-
main_context = Castle::Utils::
|
9
|
-
Castle::Utils::
|
8
|
+
main_context = Castle::Utils::Clone.call(initial_context)
|
9
|
+
Castle::Utils::Merge.call(main_context, request_context || {})
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Context
|
5
|
+
# prepares the context from the request
|
6
|
+
module Prepare
|
7
|
+
class << self
|
8
|
+
# @param request [Request]
|
9
|
+
# @param options [Hash]
|
10
|
+
# @return [Hash]
|
11
|
+
def call(request, options = {})
|
12
|
+
default_context = Castle::Context::GetDefault.new(request, options[:cookies]).call
|
13
|
+
Castle::Context::Merge.call(default_context, options[:context])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Core
|
5
|
+
# this module returns a new configured Net::HTTP object
|
6
|
+
module GetConnection
|
7
|
+
HTTPS_SCHEME = 'https'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
11
|
+
def call(config = Castle.config)
|
12
|
+
http = Net::HTTP.new(config.base_url.host, config.base_url.port)
|
13
|
+
http.read_timeout = config.request_timeout / 1000.0
|
14
|
+
|
15
|
+
if config.base_url.scheme == HTTPS_SCHEME
|
16
|
+
http.use_ssl = true
|
17
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
18
|
+
end
|
19
|
+
|
20
|
+
http
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Castle
|
4
|
-
module
|
4
|
+
module Core
|
5
5
|
# parses api response
|
6
|
-
module
|
6
|
+
module ProcessResponse
|
7
7
|
RESPONSE_ERRORS = {
|
8
8
|
400 => Castle::BadRequestError,
|
9
9
|
401 => Castle::UnauthorizedError,
|
@@ -17,6 +17,8 @@ module Castle
|
|
17
17
|
def call(response)
|
18
18
|
verify!(response)
|
19
19
|
|
20
|
+
Castle::Logger.call('response:', response.body.to_s)
|
21
|
+
|
20
22
|
return {} if response.body.nil? || response.body.empty?
|
21
23
|
|
22
24
|
begin
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Core
|
5
|
+
# Parses a webhook
|
6
|
+
module ProcessWebhook
|
7
|
+
class << self
|
8
|
+
# Checks if webhook is valid
|
9
|
+
# @param webhook [Request]
|
10
|
+
def call(webhook)
|
11
|
+
webhook.body.read.tap do |result|
|
12
|
+
raise Castle::ApiError, 'Invalid webhook from Castle API' if result.blank?
|
13
|
+
|
14
|
+
Castle::Logger.call('webhook:', result.to_s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Core
|
5
|
+
# this class is responsible for making requests to api
|
6
|
+
module SendRequest
|
7
|
+
# Default headers that we add to passed ones
|
8
|
+
DEFAULT_HEADERS = {
|
9
|
+
'Content-Type' => 'application/json'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
private_constant :DEFAULT_HEADERS
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# @param command [String]
|
16
|
+
# @param headers [Hash]
|
17
|
+
# @param http [Net::HTTP]
|
18
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
19
|
+
def call(command, headers, http = nil, config = Castle.config)
|
20
|
+
(http || Castle::Core::GetConnection.call).request(
|
21
|
+
build(
|
22
|
+
command,
|
23
|
+
headers.merge(DEFAULT_HEADERS),
|
24
|
+
config
|
25
|
+
)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param command [String]
|
30
|
+
# @param headers [Hash]
|
31
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
32
|
+
def build(command, headers, config = Castle.config)
|
33
|
+
url = "#{config.base_url.path}/#{command.path}"
|
34
|
+
request_obj = Net::HTTP.const_get(command.method.to_s.capitalize).new(url, headers)
|
35
|
+
|
36
|
+
unless command.method == :get
|
37
|
+
request_obj.body = ::Castle::Utils::CleanInvalidChars.call(
|
38
|
+
command.data
|
39
|
+
).to_json
|
40
|
+
end
|
41
|
+
|
42
|
+
Castle::Logger.call("#{url}:", request_obj.body)
|
43
|
+
|
44
|
+
request_obj.basic_auth('', config.api_secret)
|
45
|
+
request_obj
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|