castle-rb 6.0.1 → 7.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 +8 -8
- data/lib/castle.rb +7 -11
- data/lib/castle/api.rb +7 -12
- data/lib/castle/api/approve_device.rb +1 -6
- data/lib/castle/api/authenticate.rb +10 -7
- data/lib/castle/api/end_impersonation.rb +3 -8
- data/lib/castle/api/filter.rb +37 -0
- data/lib/castle/api/get_device.rb +1 -6
- data/lib/castle/api/get_devices_for_user.rb +1 -6
- data/lib/castle/api/log.rb +37 -0
- data/lib/castle/api/report_device.rb +1 -6
- data/lib/castle/api/risk.rb +37 -0
- data/lib/castle/api/start_impersonation.rb +3 -8
- data/lib/castle/api/track.rb +1 -6
- data/lib/castle/client.rb +36 -16
- data/lib/castle/commands/approve_device.rb +1 -5
- data/lib/castle/commands/end_impersonation.rb +1 -1
- data/lib/castle/commands/filter.rb +23 -0
- data/lib/castle/commands/get_device.rb +1 -5
- data/lib/castle/commands/get_devices_for_user.rb +1 -5
- data/lib/castle/commands/{identify.rb → log.rb} +4 -3
- data/lib/castle/commands/report_device.rb +1 -5
- data/lib/castle/commands/risk.rb +23 -0
- data/lib/castle/commands/start_impersonation.rb +1 -1
- data/lib/castle/configuration.rb +18 -8
- data/lib/castle/core/get_connection.rb +3 -1
- data/lib/castle/core/process_response.rb +5 -2
- data/lib/castle/core/process_webhook.rb +10 -5
- data/lib/castle/core/send_request.rb +8 -16
- data/lib/castle/errors.rb +37 -13
- data/lib/castle/failover/prepare_response.rb +2 -7
- data/lib/castle/failover/strategy.rb +3 -0
- data/lib/castle/headers/extract.rb +4 -4
- data/lib/castle/headers/filter.rb +9 -6
- data/lib/castle/ips/extract.rb +4 -2
- data/lib/castle/logger.rb +3 -3
- data/lib/castle/payload/prepare.rb +3 -4
- data/lib/castle/secure_mode.rb +3 -2
- data/lib/castle/support/hanami.rb +2 -6
- data/lib/castle/support/rails.rb +1 -3
- data/lib/castle/utils/clean_invalid_chars.rb +1 -3
- data/lib/castle/verdict.rb +2 -0
- data/lib/castle/version.rb +1 -1
- data/lib/castle/webhooks/verify.rb +9 -7
- data/spec/integration/rails/rails_spec.rb +9 -7
- data/spec/integration/rails/support/home_controller.rb +26 -24
- data/spec/lib/castle/api/approve_device_spec.rb +3 -3
- data/spec/lib/castle/api/authenticate_spec.rb +20 -24
- data/spec/lib/castle/api/end_impersonation_spec.rb +11 -5
- data/spec/lib/castle/api/filter_spec.rb +5 -0
- data/spec/lib/castle/api/get_device_spec.rb +3 -3
- data/spec/lib/castle/api/get_devices_for_user_spec.rb +3 -3
- data/spec/lib/castle/api/log_spec.rb +5 -0
- data/spec/lib/castle/api/report_device_spec.rb +3 -3
- data/spec/lib/castle/api/risk_spec.rb +5 -0
- data/spec/lib/castle/api/start_impersonation_spec.rb +11 -5
- data/spec/lib/castle/api/track_spec.rb +11 -7
- data/spec/lib/castle/api_spec.rb +4 -20
- data/spec/lib/castle/client_id/extract_spec.rb +4 -13
- data/spec/lib/castle/client_spec.rb +81 -84
- data/spec/lib/castle/commands/authenticate_spec.rb +8 -15
- data/spec/lib/castle/commands/end_impersonation_spec.rb +6 -9
- data/spec/lib/castle/commands/{identify_spec.rb → filter_spec.rb} +41 -19
- data/spec/lib/castle/commands/log_spec.rb +100 -0
- data/spec/lib/castle/commands/risk_spec.rb +100 -0
- data/spec/lib/castle/commands/start_impersonation_spec.rb +6 -9
- data/spec/lib/castle/commands/track_spec.rb +9 -18
- data/spec/lib/castle/configuration_spec.rb +2 -6
- data/spec/lib/castle/context/get_default_spec.rb +8 -8
- data/spec/lib/castle/context/prepare_spec.rb +6 -7
- data/spec/lib/castle/core/get_connection_spec.rb +6 -22
- data/spec/lib/castle/core/process_response_spec.rb +1 -8
- data/spec/lib/castle/core/send_request_spec.rb +4 -29
- data/spec/lib/castle/headers/extract_spec.rb +1 -3
- data/spec/lib/castle/headers/filter_spec.rb +12 -11
- data/spec/lib/castle/ips/extract_spec.rb +4 -13
- data/spec/lib/castle/logger_spec.rb +2 -6
- data/spec/lib/castle/payload/prepare_spec.rb +5 -4
- data/spec/lib/castle/session_spec.rb +13 -36
- data/spec/lib/castle/singleton_configuration_spec.rb +2 -6
- data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +2 -2
- data/spec/lib/castle/utils/merge_spec.rb +3 -1
- data/spec/lib/castle/validators/present_spec.rb +5 -6
- data/spec/lib/castle/webhooks/verify_spec.rb +8 -24
- data/spec/lib/castle_spec.rb +4 -10
- data/spec/spec_helper.rb +1 -3
- data/spec/support/shared_examples/action_request.rb +152 -0
- data/spec/support/shared_examples/configuration.rb +14 -42
- metadata +23 -18
- data/lib/castle/api/identify.rb +0 -26
- data/lib/castle/api/review.rb +0 -24
- data/lib/castle/commands/review.rb +0 -17
- data/lib/castle/events.rb +0 -49
- data/spec/lib/castle/api/identify_spec.rb +0 -68
- data/spec/lib/castle/api/review_spec.rb +0 -19
- data/spec/lib/castle/commands/review_spec.rb +0 -24
- data/spec/lib/castle/events_spec.rb +0 -5
@@ -9,11 +9,7 @@ module Castle
|
|
9
9
|
# @return [Castle::Command]
|
10
10
|
def build(options = {})
|
11
11
|
Castle::Validators::Present.call(options, %i[device_token])
|
12
|
-
Castle::Command.new(
|
13
|
-
"devices/#{options[:device_token]}/approve",
|
14
|
-
nil,
|
15
|
-
:put
|
16
|
-
)
|
12
|
+
Castle::Command.new("devices/#{options[:device_token]}/approve", nil, :put)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# Generates the payload for the filter request
|
6
|
+
class Filter
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[event])
|
12
|
+
context = Castle::Context::Sanitize.call(options[:context])
|
13
|
+
|
14
|
+
Castle::Command.new(
|
15
|
+
'filter',
|
16
|
+
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
17
|
+
:post
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -9,11 +9,7 @@ module Castle
|
|
9
9
|
# @return [Castle::Command]
|
10
10
|
def build(options = {})
|
11
11
|
Castle::Validators::Present.call(options, %i[device_token])
|
12
|
-
Castle::Command.new(
|
13
|
-
"devices/#{options[:device_token]}",
|
14
|
-
nil,
|
15
|
-
:get
|
16
|
-
)
|
12
|
+
Castle::Command.new("devices/#{options[:device_token]}", nil, :get)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
@@ -9,11 +9,7 @@ module Castle
|
|
9
9
|
# @return [Castle::Command]
|
10
10
|
def build(options = {})
|
11
11
|
Castle::Validators::Present.call(options, %i[user_id])
|
12
|
-
Castle::Command.new(
|
13
|
-
"users/#{options[:user_id]}/devices",
|
14
|
-
nil,
|
15
|
-
:get
|
16
|
-
)
|
12
|
+
Castle::Command.new("users/#{options[:user_id]}/devices", nil, :get)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Castle
|
4
4
|
module Commands
|
5
|
-
|
5
|
+
# Generates the payload for the log request
|
6
|
+
class Log
|
6
7
|
class << self
|
7
8
|
# @param options [Hash]
|
8
9
|
# @return [Castle::Command]
|
9
10
|
def build(options = {})
|
10
|
-
Castle::Validators::
|
11
|
+
Castle::Validators::Present.call(options, %i[event])
|
11
12
|
context = Castle::Context::Sanitize.call(options[:context])
|
12
13
|
|
13
14
|
Castle::Command.new(
|
14
|
-
'
|
15
|
+
'log',
|
15
16
|
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
16
17
|
:post
|
17
18
|
)
|
@@ -9,11 +9,7 @@ module Castle
|
|
9
9
|
# @return [Castle::Command]
|
10
10
|
def build(options = {})
|
11
11
|
Castle::Validators::Present.call(options, %i[device_token])
|
12
|
-
Castle::Command.new(
|
13
|
-
"devices/#{options[:device_token]}/report",
|
14
|
-
nil,
|
15
|
-
:put
|
16
|
-
)
|
12
|
+
Castle::Command.new("devices/#{options[:device_token]}/report", nil, :put)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
module Commands
|
5
|
+
# Generates the payload for the risk request
|
6
|
+
class Risk
|
7
|
+
class << self
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [Castle::Command]
|
10
|
+
def build(options = {})
|
11
|
+
Castle::Validators::Present.call(options, %i[event])
|
12
|
+
context = Castle::Context::Sanitize.call(options[:context])
|
13
|
+
|
14
|
+
Castle::Command.new(
|
15
|
+
'risk',
|
16
|
+
options.merge(context: context, sent_at: Castle::Utils::GetTimestamp.call),
|
17
|
+
:post
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/castle/configuration.rb
CHANGED
@@ -8,15 +8,18 @@ module Castle
|
|
8
8
|
# API endpoint
|
9
9
|
BASE_URL = 'https://api.castle.io/v1'
|
10
10
|
REQUEST_TIMEOUT = 1000 # in milliseconds
|
11
|
+
|
11
12
|
# regexp of trusted proxies which is always appended to the trusted proxy list
|
12
|
-
TRUSTED_PROXIES = [
|
13
|
+
TRUSTED_PROXIES = [
|
14
|
+
/
|
13
15
|
\A127\.0\.0\.1\Z|
|
14
16
|
\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|
|
15
17
|
\A::1\Z|\Afd[0-9a-f]{2}:.+|
|
16
18
|
\Alocalhost\Z|
|
17
19
|
\Aunix\Z|
|
18
20
|
\Aunix:
|
19
|
-
/ix
|
21
|
+
/ix
|
22
|
+
].freeze
|
20
23
|
|
21
24
|
# @note this value is not assigned as we don't recommend using a allowlist. If you need to use
|
22
25
|
# one, this constant is provided as a good default.
|
@@ -47,8 +50,14 @@ module Castle
|
|
47
50
|
].freeze
|
48
51
|
|
49
52
|
attr_accessor :request_timeout, :trust_proxy_chain, :logger
|
50
|
-
attr_reader :api_secret,
|
51
|
-
:
|
53
|
+
attr_reader :api_secret,
|
54
|
+
:allowlisted,
|
55
|
+
:denylisted,
|
56
|
+
:failover_strategy,
|
57
|
+
:ip_headers,
|
58
|
+
:trusted_proxies,
|
59
|
+
:trusted_proxy_depth,
|
60
|
+
:base_url
|
52
61
|
|
53
62
|
def initialize
|
54
63
|
@header_format = Castle::Headers::Format
|
@@ -96,7 +105,9 @@ module Castle
|
|
96
105
|
# sets trusted proxies
|
97
106
|
# @param value [Array<String,Regexp>]
|
98
107
|
def trusted_proxies=(value)
|
99
|
-
|
108
|
+
unless value.is_a?(Array)
|
109
|
+
raise Castle::ConfigurationError, 'trusted proxies must be an Array'
|
110
|
+
end
|
100
111
|
|
101
112
|
@trusted_proxies = value
|
102
113
|
end
|
@@ -111,9 +122,8 @@ module Castle
|
|
111
122
|
end
|
112
123
|
|
113
124
|
def failover_strategy=(value)
|
114
|
-
@failover_strategy =
|
115
|
-
strategy == value.to_sym
|
116
|
-
end
|
125
|
+
@failover_strategy =
|
126
|
+
Castle::Failover::STRATEGIES.detect { |strategy| strategy == value.to_sym }
|
117
127
|
raise Castle::ConfigurationError, 'unrecognized failover strategy' if @failover_strategy.nil?
|
118
128
|
end
|
119
129
|
|
@@ -8,7 +8,9 @@ module Castle
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
11
|
-
|
11
|
+
# @return [Net::HTTP]
|
12
|
+
def call(config = nil)
|
13
|
+
config ||= Castle.config
|
12
14
|
http = Net::HTTP.new(config.base_url.host, config.base_url.port)
|
13
15
|
http.read_timeout = config.request_timeout / 1000.0
|
14
16
|
|
@@ -14,10 +14,13 @@ 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
|
|
20
|
-
Castle::Logger.call('response:', response.body.to_s)
|
23
|
+
Castle::Logger.call('response:', response.body.to_s, config)
|
21
24
|
|
22
25
|
return {} if response.body.nil? || response.body.empty?
|
23
26
|
|
@@ -7,12 +7,17 @@ module Castle
|
|
7
7
|
class << self
|
8
8
|
# Checks if webhook is valid
|
9
9
|
# @param webhook [Request]
|
10
|
-
|
11
|
-
|
12
|
-
|
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?
|
13
18
|
|
14
|
-
|
15
|
-
|
19
|
+
Castle::Logger.call('webhook:', result.to_s, config)
|
20
|
+
end
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -5,9 +5,7 @@ module Castle
|
|
5
5
|
# this class is responsible for making requests to api
|
6
6
|
module SendRequest
|
7
7
|
# Default headers that we add to passed ones
|
8
|
-
DEFAULT_HEADERS = {
|
9
|
-
'Content-Type' => 'application/json'
|
10
|
-
}.freeze
|
8
|
+
DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze
|
11
9
|
|
12
10
|
private_constant :DEFAULT_HEADERS
|
13
11
|
|
@@ -15,31 +13,25 @@ module Castle
|
|
15
13
|
# @param command [String]
|
16
14
|
# @param headers [Hash]
|
17
15
|
# @param http [Net::HTTP]
|
18
|
-
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
19
|
-
def call(command, headers, http = nil, config =
|
16
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
17
|
+
def call(command, headers, http = nil, config = nil)
|
20
18
|
(http || Castle::Core::GetConnection.call).request(
|
21
|
-
build(
|
22
|
-
command,
|
23
|
-
headers.merge(DEFAULT_HEADERS),
|
24
|
-
config
|
25
|
-
)
|
19
|
+
build(command, headers.merge(DEFAULT_HEADERS), config)
|
26
20
|
)
|
27
21
|
end
|
28
22
|
|
29
23
|
# @param command [String]
|
30
24
|
# @param headers [Hash]
|
31
|
-
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
32
|
-
def build(command, headers, config
|
25
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
26
|
+
def build(command, headers, config)
|
33
27
|
url = "#{config.base_url.path}/#{command.path}"
|
34
28
|
request_obj = Net::HTTP.const_get(command.method.to_s.capitalize).new(url, headers)
|
35
29
|
|
36
30
|
unless command.method == :get
|
37
|
-
request_obj.body = ::Castle::Utils::CleanInvalidChars.call(
|
38
|
-
command.data
|
39
|
-
).to_json
|
31
|
+
request_obj.body = ::Castle::Utils::CleanInvalidChars.call(command.data).to_json
|
40
32
|
end
|
41
33
|
|
42
|
-
Castle::Logger.call("#{url}:", request_obj.body)
|
34
|
+
Castle::Logger.call("#{url}:", request_obj.body, config)
|
43
35
|
|
44
36
|
request_obj.basic_auth('', config.api_secret)
|
45
37
|
request_obj
|
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,30 +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
|
+
|
23
32
|
# webhook signature verification error
|
24
|
-
class WebhookVerificationError < Castle::Error
|
33
|
+
class WebhookVerificationError < Castle::Error
|
34
|
+
end
|
25
35
|
|
26
36
|
# api error bad request 400
|
27
|
-
class BadRequestError < Castle::ApiError
|
37
|
+
class BadRequestError < Castle::ApiError
|
38
|
+
end
|
39
|
+
|
28
40
|
# api error forbidden 403
|
29
|
-
class ForbiddenError < Castle::ApiError
|
41
|
+
class ForbiddenError < Castle::ApiError
|
42
|
+
end
|
43
|
+
|
30
44
|
# api error not found 404
|
31
|
-
class NotFoundError < Castle::ApiError
|
45
|
+
class NotFoundError < Castle::ApiError
|
46
|
+
end
|
47
|
+
|
32
48
|
# api error user unauthorized 419
|
33
|
-
class UserUnauthorizedError < Castle::ApiError
|
49
|
+
class UserUnauthorizedError < Castle::ApiError
|
50
|
+
end
|
51
|
+
|
34
52
|
# api error invalid param 422
|
35
|
-
class InvalidParametersError < Castle::ApiError
|
53
|
+
class InvalidParametersError < Castle::ApiError
|
54
|
+
end
|
55
|
+
|
36
56
|
# api error unauthorized 401
|
37
|
-
class UnauthorizedError < Castle::ApiError
|
57
|
+
class UnauthorizedError < Castle::ApiError
|
58
|
+
end
|
59
|
+
|
38
60
|
# all internal server errors
|
39
|
-
class InternalServerError < Castle::ApiError
|
61
|
+
class InternalServerError < Castle::ApiError
|
62
|
+
end
|
40
63
|
|
41
64
|
# impersonation command failed
|
42
|
-
class ImpersonationFailed < Castle::ApiError
|
65
|
+
class ImpersonationFailed < Castle::ApiError
|
66
|
+
end
|
43
67
|
end
|
@@ -4,19 +4,14 @@ module Castle
|
|
4
4
|
module Failover
|
5
5
|
# generate failover authentication response
|
6
6
|
class PrepareResponse
|
7
|
-
def initialize(user_id, reason:, strategy:
|
7
|
+
def initialize(user_id, reason:, strategy:)
|
8
8
|
@strategy = strategy
|
9
9
|
@reason = reason
|
10
10
|
@user_id = user_id
|
11
11
|
end
|
12
12
|
|
13
13
|
def call
|
14
|
-
{
|
15
|
-
action: @strategy.to_s,
|
16
|
-
user_id: @user_id,
|
17
|
-
failover: true,
|
18
|
-
failover_reason: @reason
|
19
|
-
}
|
14
|
+
{ action: @strategy.to_s, user_id: @user_id, failover: true, failover_reason: @reason }
|
20
15
|
end
|
21
16
|
end
|
22
17
|
end
|
@@ -13,11 +13,11 @@ module Castle
|
|
13
13
|
private_constant :ALWAYS_ALLOWLISTED, :ALWAYS_DENYLISTED
|
14
14
|
|
15
15
|
# @param headers [Hash]
|
16
|
-
# @param config [Castle::Configuration, Castle::SingletonConfiguration]
|
17
|
-
def initialize(headers, config =
|
16
|
+
# @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
|
17
|
+
def initialize(headers, config = nil)
|
18
18
|
@headers = headers
|
19
|
-
@config = config
|
20
|
-
@no_allowlist = config.allowlisted.empty?
|
19
|
+
@config = config || Castle.config
|
20
|
+
@no_allowlist = @config.allowlisted.empty?
|
21
21
|
end
|
22
22
|
|
23
23
|
# Serialize HTTP headers
|
@@ -12,7 +12,8 @@ module Castle
|
|
12
12
|
HTTP(?:_|-).*|
|
13
13
|
CONTENT(?:_|-)LENGTH|
|
14
14
|
REMOTE(?:_|-)ADDR
|
15
|
-
$/xi
|
15
|
+
$/xi
|
16
|
+
.freeze
|
16
17
|
|
17
18
|
private_constant :VALUABLE_HEADERS
|
18
19
|
|
@@ -25,12 +26,14 @@ module Castle
|
|
25
26
|
# Serialize HTTP headers
|
26
27
|
# @return [Hash]
|
27
28
|
def call
|
28
|
-
@request_env
|
29
|
-
|
29
|
+
@request_env
|
30
|
+
.keys
|
31
|
+
.each_with_object({}) do |header_name, acc|
|
32
|
+
next unless header_name.match(VALUABLE_HEADERS)
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
formatted_name = @header_format.call(header_name)
|
35
|
+
acc[formatted_name] = @request_env[header_name]
|
36
|
+
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|