castle-rb 5.0.0 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +113 -39
- data/lib/castle.rb +49 -29
- data/lib/castle/api.rb +20 -16
- data/lib/castle/api/approve_device.rb +20 -0
- data/lib/castle/api/authenticate.rb +37 -0
- data/lib/castle/api/end_impersonation.rb +24 -0
- data/lib/castle/api/filter.rb +37 -0
- data/lib/castle/api/get_device.rb +20 -0
- data/lib/castle/api/get_devices_for_user.rb +20 -0
- data/lib/castle/api/log.rb +37 -0
- data/lib/castle/api/report_device.rb +20 -0
- data/lib/castle/api/risk.rb +37 -0
- data/lib/castle/api/start_impersonation.rb +24 -0
- data/lib/castle/api/track.rb +21 -0
- data/lib/castle/client.rb +74 -68
- data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
- data/lib/castle/commands/approve_device.rb +17 -0
- data/lib/castle/commands/authenticate.rb +13 -13
- data/lib/castle/commands/end_impersonation.rb +25 -0
- data/lib/castle/commands/filter.rb +22 -0
- data/lib/castle/commands/get_device.rb +17 -0
- data/lib/castle/commands/get_devices_for_user.rb +17 -0
- data/lib/castle/commands/log.rb +22 -0
- data/lib/castle/commands/report_device.rb +17 -0
- data/lib/castle/commands/risk.rb +22 -0
- data/lib/castle/commands/start_impersonation.rb +25 -0
- data/lib/castle/commands/track.rb +12 -13
- data/lib/castle/configuration.rb +31 -23
- 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 +27 -0
- data/lib/castle/{api/response.rb → core/process_response.rb} +8 -3
- data/lib/castle/core/process_webhook.rb +25 -0
- data/lib/castle/core/send_request.rb +42 -0
- data/lib/castle/errors.rb +38 -12
- data/lib/castle/failover/prepare_response.rb +28 -0
- data/lib/castle/failover/strategy.rb +23 -0
- data/lib/castle/{extractors/headers.rb → headers/extract.rb} +8 -6
- data/lib/castle/headers/filter.rb +40 -0
- data/lib/castle/headers/format.rb +24 -0
- data/lib/castle/{extractors/ip.rb → ips/extract.rb} +11 -7
- data/lib/castle/logger.rb +19 -0
- data/lib/castle/payload/prepare.rb +26 -0
- data/lib/castle/secure_mode.rb +7 -2
- data/lib/castle/session.rb +18 -0
- data/lib/castle/singleton_configuration.rb +9 -0
- data/lib/castle/support/hanami.rb +2 -6
- data/lib/castle/support/rails.rb +1 -3
- data/lib/castle/utils/clean_invalid_chars.rb +22 -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 +15 -0
- data/lib/castle/version.rb +1 -1
- data/lib/castle/webhooks/verify.rb +45 -0
- data/spec/integration/rails/rails_spec.rb +42 -14
- data/spec/integration/rails/support/application.rb +3 -1
- data/spec/integration/rails/support/home_controller.rb +50 -6
- data/spec/lib/castle/api/approve_device_spec.rb +21 -0
- data/spec/lib/castle/api/authenticate_spec.rb +136 -0
- data/spec/lib/castle/api/end_impersonation_spec.rb +65 -0
- data/spec/lib/castle/api/filter_spec.rb +5 -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/log_spec.rb +5 -0
- data/spec/lib/castle/api/report_device_spec.rb +21 -0
- data/spec/lib/castle/api/risk_spec.rb +5 -0
- data/spec/lib/castle/api/start_impersonation_spec.rb +65 -0
- data/spec/lib/castle/api/track_spec.rb +72 -0
- data/spec/lib/castle/api_spec.rb +14 -15
- data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +6 -15
- data/spec/lib/castle/client_spec.rb +110 -92
- data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
- data/spec/lib/castle/commands/authenticate_spec.rb +15 -31
- data/spec/lib/castle/commands/end_impersonation_spec.rb +79 -0
- data/spec/lib/castle/commands/filter_spec.rb +72 -0
- data/spec/lib/castle/commands/get_device_spec.rb +24 -0
- data/spec/lib/castle/commands/{review_spec.rb → get_devices_for_user_spec.rb} +7 -7
- data/spec/lib/castle/commands/log_spec.rb +73 -0
- data/spec/lib/castle/commands/report_device_spec.rb +24 -0
- data/spec/lib/castle/commands/risk_spec.rb +73 -0
- data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +13 -41
- data/spec/lib/castle/commands/track_spec.rb +14 -34
- data/spec/lib/castle/configuration_spec.rb +8 -141
- data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +9 -10
- data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
- data/spec/lib/castle/context/prepare_spec.rb +43 -0
- data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
- data/spec/lib/castle/core/get_connection_spec.rb +43 -0
- data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +49 -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} +16 -37
- data/spec/lib/castle/failover/strategy_spec.rb +12 -0
- data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +7 -9
- data/spec/lib/castle/headers/filter_spec.rb +39 -0
- data/spec/lib/castle/headers/format_spec.rb +25 -0
- data/spec/lib/castle/{extractors/ip_spec.rb → ips/extract_spec.rb} +5 -14
- data/spec/lib/castle/logger_spec.rb +38 -0
- data/spec/lib/castle/payload/prepare_spec.rb +55 -0
- data/spec/lib/castle/session_spec.rb +65 -0
- data/spec/lib/castle/singleton_configuration_spec.rb +14 -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/merge_spec.rb +15 -0
- data/spec/lib/castle/validators/present_spec.rb +5 -6
- data/spec/lib/castle/verdict_spec.rb +9 -0
- data/spec/lib/castle/webhooks/verify_spec.rb +53 -0
- data/spec/lib/castle_spec.rb +4 -10
- data/spec/spec_helper.rb +3 -3
- data/spec/support/shared_examples/action_request.rb +155 -0
- data/spec/support/shared_examples/configuration.rb +101 -0
- metadata +144 -67
- 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/identify.rb +0 -23
- data/lib/castle/commands/impersonate.rb +0 -26
- data/lib/castle/commands/review.rb +0 -14
- data/lib/castle/events.rb +0 -49
- 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/api/connection_spec.rb +0 -59
- data/spec/lib/castle/api/session_spec.rb +0 -86
- data/spec/lib/castle/commands/identify_spec.rb +0 -88
- data/spec/lib/castle/events_spec.rb +0 -5
- data/spec/lib/castle/headers_filter_spec.rb +0 -38
- data/spec/lib/castle/headers_formatter_spec.rb +0 -25
- data/spec/lib/castle/review_spec.rb +0 -19
- data/spec/lib/castle/utils/merger_spec.rb +0 -13
- data/spec/lib/castle/utils_spec.rb +0 -156
@@ -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,27 @@
|
|
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
|
+
# @return [Net::HTTP]
|
12
|
+
def call(config = nil)
|
13
|
+
config ||= Castle.config
|
14
|
+
http = Net::HTTP.new(config.base_url.host, config.base_url.port)
|
15
|
+
http.read_timeout = config.request_timeout / 1000.0
|
16
|
+
|
17
|
+
if config.base_url.scheme == HTTPS_SCHEME
|
18
|
+
http.use_ssl = true
|
19
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
20
|
+
end
|
21
|
+
|
22
|
+
http
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
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,
|
@@ -14,9 +14,14 @@ module Castle
|
|
14
14
|
}.freeze
|
15
15
|
|
16
16
|
class << self
|
17
|
-
|
17
|
+
# @param response [Response]
|
18
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
19
|
+
# @return [Hash]
|
20
|
+
def call(response, config = nil)
|
18
21
|
verify!(response)
|
19
22
|
|
23
|
+
Castle::Logger.call('response:', response.body.to_s, config)
|
24
|
+
|
20
25
|
return {} if response.body.nil? || response.body.empty?
|
21
26
|
|
22
27
|
begin
|
@@ -0,0 +1,25 @@
|
|
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
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
11
|
+
# @return [String]
|
12
|
+
def call(webhook, config = nil)
|
13
|
+
webhook
|
14
|
+
.body
|
15
|
+
.read
|
16
|
+
.tap do |result|
|
17
|
+
raise Castle::ApiError, 'Invalid webhook from Castle API' if result.blank?
|
18
|
+
|
19
|
+
Castle::Logger.call('webhook:', result.to_s, config)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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 = { 'Content-Type' => 'application/json' }.freeze
|
9
|
+
|
10
|
+
private_constant :DEFAULT_HEADERS
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# @param command [String]
|
14
|
+
# @param headers [Hash]
|
15
|
+
# @param http [Net::HTTP]
|
16
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
17
|
+
def call(command, headers, http = nil, config = nil)
|
18
|
+
(http || Castle::Core::GetConnection.call).request(
|
19
|
+
build(command, headers.merge(DEFAULT_HEADERS), config)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param command [String]
|
24
|
+
# @param headers [Hash]
|
25
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
26
|
+
def build(command, headers, config)
|
27
|
+
url = "#{config.base_url.path}/#{command.path}"
|
28
|
+
request_obj = Net::HTTP.const_get(command.method.to_s.capitalize).new(url, headers)
|
29
|
+
|
30
|
+
unless command.method == :get
|
31
|
+
request_obj.body = ::Castle::Utils::CleanInvalidChars.call(command.data).to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
Castle::Logger.call("#{url}:", request_obj.body, config)
|
35
|
+
|
36
|
+
request_obj.basic_auth('', config.api_secret)
|
37
|
+
request_obj
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/castle/errors.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
# general error
|
5
|
-
class Error < RuntimeError
|
5
|
+
class Error < RuntimeError
|
6
|
+
end
|
7
|
+
|
6
8
|
# Raised when anything is wrong with the request (any unhappy path)
|
7
9
|
# This error indicates that either we would wait too long for a response or something
|
8
10
|
# else happened somewhere in the middle and we weren't able to get the results
|
@@ -14,28 +16,52 @@ module Castle
|
|
14
16
|
@reason = reason
|
15
17
|
end
|
16
18
|
end
|
19
|
+
|
17
20
|
# security error
|
18
|
-
class SecurityError < Castle::Error
|
21
|
+
class SecurityError < Castle::Error
|
22
|
+
end
|
23
|
+
|
19
24
|
# wrong configuration error
|
20
|
-
class ConfigurationError < Castle::Error
|
25
|
+
class ConfigurationError < Castle::Error
|
26
|
+
end
|
27
|
+
|
21
28
|
# error returned by api
|
22
|
-
class ApiError < Castle::Error
|
29
|
+
class ApiError < Castle::Error
|
30
|
+
end
|
31
|
+
|
32
|
+
# webhook signature verification error
|
33
|
+
class WebhookVerificationError < Castle::Error
|
34
|
+
end
|
23
35
|
|
24
36
|
# api error bad request 400
|
25
|
-
class BadRequestError < Castle::ApiError
|
37
|
+
class BadRequestError < Castle::ApiError
|
38
|
+
end
|
39
|
+
|
26
40
|
# api error forbidden 403
|
27
|
-
class ForbiddenError < Castle::ApiError
|
41
|
+
class ForbiddenError < Castle::ApiError
|
42
|
+
end
|
43
|
+
|
28
44
|
# api error not found 404
|
29
|
-
class NotFoundError < Castle::ApiError
|
45
|
+
class NotFoundError < Castle::ApiError
|
46
|
+
end
|
47
|
+
|
30
48
|
# api error user unauthorized 419
|
31
|
-
class UserUnauthorizedError < Castle::ApiError
|
49
|
+
class UserUnauthorizedError < Castle::ApiError
|
50
|
+
end
|
51
|
+
|
32
52
|
# api error invalid param 422
|
33
|
-
class InvalidParametersError < Castle::ApiError
|
53
|
+
class InvalidParametersError < Castle::ApiError
|
54
|
+
end
|
55
|
+
|
34
56
|
# api error unauthorized 401
|
35
|
-
class UnauthorizedError < Castle::ApiError
|
57
|
+
class UnauthorizedError < Castle::ApiError
|
58
|
+
end
|
59
|
+
|
36
60
|
# all internal server errors
|
37
|
-
class InternalServerError < Castle::ApiError
|
61
|
+
class InternalServerError < Castle::ApiError
|
62
|
+
end
|
38
63
|
|
39
64
|
# impersonation command failed
|
40
|
-
class ImpersonationFailed < Castle::ApiError
|
65
|
+
class ImpersonationFailed < Castle::ApiError
|
66
|
+
end
|
41
67
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Failover
|
5
|
+
# generate failover authentication response
|
6
|
+
class PrepareResponse
|
7
|
+
def initialize(user_id, reason:, strategy:)
|
8
|
+
@strategy = strategy
|
9
|
+
@reason = reason
|
10
|
+
@user_id = user_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
{
|
15
|
+
# v1/risk v1/filter structure
|
16
|
+
policy: {
|
17
|
+
action: @strategy.to_s
|
18
|
+
},
|
19
|
+
# v1/authenticate structure
|
20
|
+
action: @strategy.to_s,
|
21
|
+
user_id: @user_id,
|
22
|
+
failover: true,
|
23
|
+
failover_reason: @reason
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Failover
|
5
|
+
# handles failover strategy consts
|
6
|
+
module Strategy
|
7
|
+
# allow
|
8
|
+
ALLOW = :allow
|
9
|
+
|
10
|
+
# deny
|
11
|
+
DENY = :deny
|
12
|
+
|
13
|
+
# challenge
|
14
|
+
CHALLENGE = :challenge
|
15
|
+
|
16
|
+
# throw an error
|
17
|
+
THROW = :throw
|
18
|
+
end
|
19
|
+
|
20
|
+
# list of possible strategies
|
21
|
+
STRATEGIES = %i[allow deny challenge throw].freeze
|
22
|
+
end
|
23
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Castle
|
4
|
-
module
|
4
|
+
module Headers
|
5
5
|
# used for extraction of cookies and headers from the request
|
6
|
-
class
|
6
|
+
class Extract
|
7
7
|
# Headers that we will never scrub, even if they land on the configuration denylist.
|
8
8
|
ALWAYS_ALLOWLISTED = %w[User-Agent].freeze
|
9
9
|
|
@@ -13,9 +13,11 @@ module Castle
|
|
13
13
|
private_constant :ALWAYS_ALLOWLISTED, :ALWAYS_DENYLISTED
|
14
14
|
|
15
15
|
# @param headers [Hash]
|
16
|
-
|
16
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
17
|
+
def initialize(headers, config = nil)
|
17
18
|
@headers = headers
|
18
|
-
@
|
19
|
+
@config = config || Castle.config
|
20
|
+
@no_allowlist = @config.allowlisted.empty?
|
19
21
|
end
|
20
22
|
|
21
23
|
# Serialize HTTP headers
|
@@ -35,8 +37,8 @@ module Castle
|
|
35
37
|
def header_value(name, value)
|
36
38
|
return true if ALWAYS_DENYLISTED.include?(name)
|
37
39
|
return value if ALWAYS_ALLOWLISTED.include?(name)
|
38
|
-
return true if
|
39
|
-
return value if @no_allowlist ||
|
40
|
+
return true if @config.denylisted.include?(name)
|
41
|
+
return value if @no_allowlist || @config.allowlisted.include?(name)
|
40
42
|
|
41
43
|
true
|
42
44
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Headers
|
5
|
+
# used for preparing valuable headers list
|
6
|
+
class Filter
|
7
|
+
# headers filter
|
8
|
+
# HTTP_ - this is how Rack prefixes incoming HTTP headers
|
9
|
+
# CONTENT_LENGTH - for responses without Content-Length or Transfer-Encoding header
|
10
|
+
# REMOTE_ADDR - ip address header returned by web server
|
11
|
+
VALUABLE_HEADERS = /^
|
12
|
+
HTTP(?:_|-).*|
|
13
|
+
CONTENT(?:_|-)LENGTH|
|
14
|
+
REMOTE(?:_|-)ADDR
|
15
|
+
$/xi
|
16
|
+
.freeze
|
17
|
+
|
18
|
+
private_constant :VALUABLE_HEADERS
|
19
|
+
|
20
|
+
# @param request [Rack::Request]
|
21
|
+
def initialize(request)
|
22
|
+
@request_env = request.env
|
23
|
+
@header_format = Castle::Headers::Format
|
24
|
+
end
|
25
|
+
|
26
|
+
# Serialize HTTP headers
|
27
|
+
# @return [Hash]
|
28
|
+
def call
|
29
|
+
@request_env
|
30
|
+
.keys
|
31
|
+
.each_with_object({}) do |header_name, acc|
|
32
|
+
next unless header_name.match(VALUABLE_HEADERS)
|
33
|
+
|
34
|
+
formatted_name = @header_format.call(header_name)
|
35
|
+
acc[formatted_name] = @request_env[header_name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Headers
|
5
|
+
# formats header name
|
6
|
+
class Format
|
7
|
+
class << self
|
8
|
+
# @param header [String]
|
9
|
+
# @return [String]
|
10
|
+
def call(header)
|
11
|
+
format(header.to_s.gsub(/^HTTP(?:_|-)/i, ''))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# @param header [String]
|
17
|
+
# @return [String]
|
18
|
+
def format(header)
|
19
|
+
header.split(/_|-/).map(&:capitalize).join('-')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,23 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Castle
|
4
|
-
module
|
4
|
+
# IPs-related module
|
5
|
+
module IPs
|
5
6
|
# used for extraction of ip from the request
|
6
|
-
class
|
7
|
+
class Extract
|
7
8
|
# ordered list of ip headers for ip extraction
|
8
9
|
DEFAULT = %w[X-Forwarded-For Remote-Addr].freeze
|
10
|
+
|
9
11
|
# list of header which are used with proxy depth setting
|
10
12
|
DEPTH_RELATED = %w[X-Forwarded-For].freeze
|
11
13
|
|
12
14
|
private_constant :DEFAULT
|
13
15
|
|
14
16
|
# @param headers [Hash]
|
15
|
-
|
17
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
18
|
+
def initialize(headers, config = nil)
|
19
|
+
config ||= Castle.config
|
16
20
|
@headers = headers
|
17
|
-
@ip_headers =
|
18
|
-
@proxies =
|
19
|
-
@trust_proxy_chain =
|
20
|
-
@trusted_proxy_depth =
|
21
|
+
@ip_headers = config.ip_headers.empty? ? DEFAULT : config.ip_headers
|
22
|
+
@proxies = config.trusted_proxies + Castle::Configuration::TRUSTED_PROXIES
|
23
|
+
@trust_proxy_chain = config.trust_proxy_chain
|
24
|
+
@trusted_proxy_depth = config.trusted_proxy_depth
|
21
25
|
end
|
22
26
|
|
23
27
|
# Order of headers:
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
# module for logger handling
|
5
|
+
module Logger
|
6
|
+
class << self
|
7
|
+
# @param message [String]
|
8
|
+
# @param data [String]
|
9
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
10
|
+
def call(message, data = nil, config = nil)
|
11
|
+
logger = (config || Castle.config).logger
|
12
|
+
|
13
|
+
return unless logger
|
14
|
+
|
15
|
+
logger.info("[CASTLE] #{message} #{data}".strip)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|