castle-rb 3.6.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -5
- data/lib/castle.rb +3 -1
- data/lib/castle/client.rb +20 -15
- data/lib/castle/configuration.rb +30 -3
- data/lib/castle/context/default.rb +36 -18
- data/lib/castle/context/sanitizer.rb +1 -0
- data/lib/castle/events.rb +49 -0
- data/lib/castle/extractors/client_id.rb +7 -3
- data/lib/castle/extractors/headers.rb +24 -30
- data/lib/castle/extractors/ip.rb +49 -5
- data/lib/castle/header_filter.rb +35 -0
- data/lib/castle/header_formatter.rb +3 -0
- data/lib/castle/validators/not_supported.rb +1 -0
- data/lib/castle/validators/present.rb +1 -0
- data/lib/castle/version.rb +1 -1
- data/spec/integration/rails/rails_spec.rb +61 -0
- data/spec/integration/rails/support/all.rb +6 -0
- data/spec/integration/rails/support/application.rb +15 -0
- data/spec/integration/rails/support/home_controller.rb +21 -0
- data/spec/lib/castle/api/request/build_spec.rb +4 -2
- data/spec/lib/castle/api/request_spec.rb +1 -0
- data/spec/lib/castle/client_spec.rb +5 -3
- data/spec/lib/castle/commands/authenticate_spec.rb +1 -0
- data/spec/lib/castle/commands/identify_spec.rb +1 -0
- data/spec/lib/castle/commands/impersonate_spec.rb +1 -0
- data/spec/lib/castle/commands/track_spec.rb +1 -0
- data/spec/lib/castle/configuration_spec.rb +18 -2
- data/spec/lib/castle/context/default_spec.rb +10 -11
- data/spec/lib/castle/events_spec.rb +5 -0
- data/spec/lib/castle/extractors/client_id_spec.rb +2 -1
- data/spec/lib/castle/extractors/headers_spec.rb +66 -49
- data/spec/lib/castle/extractors/ip_spec.rb +56 -12
- data/spec/lib/castle/header_filter_spec.rb +38 -0
- data/spec/lib/castle/header_formatter_spec.rb +1 -1
- data/spec/lib/castle/utils/cloner_spec.rb +1 -0
- data/spec/lib/castle/utils/timestamp_spec.rb +3 -4
- data/spec/lib/castle/utils_spec.rb +1 -1
- data/spec/lib/castle/validators/not_supported_spec.rb +1 -3
- metadata +31 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40234604d033086c79d951cbec5a52902df4ccda433eff1749c20cfeaca114d6
|
4
|
+
data.tar.gz: e46b4d91acbcf6c370de3a0804d3f9e75ad86bc181b40e2727e92d5d63579953
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d70f1b03fb44e3d23afcb03ca83165f5b521a53269f34d2ed63740cc181e110da3692db50d6f701477a24430262d08859a71f7faeae8867c8962242d7421545d
|
7
|
+
data.tar.gz: b4a32a603c55df97450e59f936677d5b56387b8d4bf145d4355ca7f5ec606c557d00da72a92bb3f2b641d93a3d3f6e21b9f9412fff35f1840c43db66789b3b6c
|
data/README.md
CHANGED
@@ -85,20 +85,51 @@ Castle.configure do |config|
|
|
85
85
|
# We always blacklist Cookie and Authentication headers. If you use any other headers that
|
86
86
|
# might contain sensitive information, you should blacklist them.
|
87
87
|
config.blacklisted = ['HTTP-X-header']
|
88
|
+
|
89
|
+
# 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
|
+
# and not the address of a reverse proxy server, you can define trusted proxies
|
97
|
+
# 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
|
88
101
|
end
|
89
102
|
```
|
90
103
|
|
104
|
+
## Event Context
|
105
|
+
|
91
106
|
The client will automatically configure the context for each request.
|
92
107
|
|
108
|
+
### Overriding Default Context Properties
|
109
|
+
|
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:
|
111
|
+
```ruby
|
112
|
+
request_context = ::Castle::Client.to_context(request)
|
113
|
+
track_options = ::Castle::Client.to_options({
|
114
|
+
event: ::Castle::Events::LOGIN_SUCCEEDED,
|
115
|
+
user_id: user.id,
|
116
|
+
properties: {
|
117
|
+
key: 'value'
|
118
|
+
},
|
119
|
+
user_traits: {
|
120
|
+
key: 'value'
|
121
|
+
}
|
122
|
+
})
|
123
|
+
```
|
124
|
+
|
93
125
|
## Tracking
|
94
126
|
|
95
127
|
Here is a simple example of a track event.
|
96
128
|
|
97
|
-
|
98
129
|
```ruby
|
99
130
|
begin
|
100
131
|
castle.track(
|
101
|
-
event:
|
132
|
+
event: ::Castle::Events::LOGIN_SUCCEEDED,
|
102
133
|
user_id: user.id
|
103
134
|
)
|
104
135
|
rescue Castle::Error => e
|
@@ -132,7 +163,7 @@ end
|
|
132
163
|
```ruby
|
133
164
|
request_context = ::Castle::Client.to_context(request)
|
134
165
|
track_options = ::Castle::Client.to_options({
|
135
|
-
event:
|
166
|
+
event: ::Castle::Events::LOGIN_SUCCEEDED,
|
136
167
|
user_id: user.id,
|
137
168
|
properties: {
|
138
169
|
key: 'value'
|
@@ -144,13 +175,18 @@ track_options = ::Castle::Client.to_options({
|
|
144
175
|
CastleTrackingWorker.perform_async(request_context, track_options)
|
145
176
|
```
|
146
177
|
|
178
|
+
## Events
|
179
|
+
|
180
|
+
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
|
+
|
147
182
|
## Impersonation mode
|
148
183
|
|
149
|
-
https://castle.io/docs/
|
184
|
+
https://castle.io/docs/impersonation_mode
|
150
185
|
|
151
186
|
## Exceptions
|
152
187
|
|
153
|
-
`Castle::Error` will be thrown if the Castle API returns a 400 or a 500 level HTTP response.
|
188
|
+
`Castle::Error` will be thrown if the Castle API returns a 400 or a 500 level HTTP response.
|
189
|
+
You can also choose to catch a more [finegrained error](https://github.com/castle/castle-ruby/blob/master/lib/castle/errors.rb).
|
154
190
|
|
155
191
|
## Documentation
|
156
192
|
|
data/lib/castle.rb
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
%w[
|
11
11
|
castle/version
|
12
|
+
castle/events
|
12
13
|
castle/errors
|
13
14
|
castle/command
|
14
15
|
castle/utils
|
@@ -28,6 +29,7 @@
|
|
28
29
|
castle/configuration
|
29
30
|
castle/failover_auth_response
|
30
31
|
castle/client
|
32
|
+
castle/header_filter
|
31
33
|
castle/header_formatter
|
32
34
|
castle/secure_mode
|
33
35
|
castle/extractors/client_id
|
@@ -52,7 +54,7 @@ module Castle
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def config
|
55
|
-
@
|
57
|
+
@config ||= Castle::Configuration.new
|
56
58
|
end
|
57
59
|
|
58
60
|
def api_secret=(api_secret)
|
data/lib/castle/client.rb
CHANGED
@@ -23,6 +23,7 @@ module Castle
|
|
23
23
|
|
24
24
|
def failover_response_or_raise(failover_response, error)
|
25
25
|
return failover_response.generate unless Castle.config.failover_strategy == :throw
|
26
|
+
|
26
27
|
raise error
|
27
28
|
end
|
28
29
|
end
|
@@ -38,21 +39,16 @@ module Castle
|
|
38
39
|
def authenticate(options = {})
|
39
40
|
options = Castle::Utils.deep_symbolize_keys(options || {})
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
)
|
50
|
-
|
51
|
-
else
|
52
|
-
FailoverAuthResponse.new(
|
53
|
-
options[:user_id],
|
54
|
-
strategy: :allow, reason: 'Castle set to do not track.'
|
55
|
-
).generate
|
42
|
+
return generate_do_not_track_response(options[:user_id]) unless tracked?
|
43
|
+
|
44
|
+
add_timestamp_if_necessary(options)
|
45
|
+
command = Castle::Commands::Authenticate.new(@context).build(options)
|
46
|
+
begin
|
47
|
+
Castle::API.request(command).merge(failover: false, failover_reason: nil)
|
48
|
+
rescue Castle::RequestError, Castle::InternalServerError => e
|
49
|
+
self.class.failover_response_or_raise(
|
50
|
+
FailoverAuthResponse.new(options[:user_id], reason: e.to_s), e
|
51
|
+
)
|
56
52
|
end
|
57
53
|
end
|
58
54
|
|
@@ -60,6 +56,7 @@ module Castle
|
|
60
56
|
options = Castle::Utils.deep_symbolize_keys(options || {})
|
61
57
|
|
62
58
|
return unless tracked?
|
59
|
+
|
63
60
|
add_timestamp_if_necessary(options)
|
64
61
|
|
65
62
|
command = Castle::Commands::Identify.new(@context).build(options)
|
@@ -70,6 +67,7 @@ module Castle
|
|
70
67
|
options = Castle::Utils.deep_symbolize_keys(options || {})
|
71
68
|
|
72
69
|
return unless tracked?
|
70
|
+
|
73
71
|
add_timestamp_if_necessary(options)
|
74
72
|
|
75
73
|
command = Castle::Commands::Track.new(@context).build(options)
|
@@ -99,6 +97,13 @@ module Castle
|
|
99
97
|
|
100
98
|
private
|
101
99
|
|
100
|
+
def generate_do_not_track_response(user_id)
|
101
|
+
FailoverAuthResponse.new(
|
102
|
+
user_id,
|
103
|
+
strategy: :allow, reason: 'Castle is set to do not track.'
|
104
|
+
).generate
|
105
|
+
end
|
106
|
+
|
102
107
|
def add_timestamp_if_necessary(options)
|
103
108
|
options[:timestamp] ||= @timestamp if @timestamp
|
104
109
|
end
|
data/lib/castle/configuration.rb
CHANGED
@@ -9,6 +9,15 @@ module Castle
|
|
9
9
|
FAILOVER_STRATEGY = :allow
|
10
10
|
REQUEST_TIMEOUT = 500 # in milliseconds
|
11
11
|
FAILOVER_STRATEGIES = %i[allow deny challenge throw].freeze
|
12
|
+
# regexp of trusted proxies which is always appended to the trusted proxy list
|
13
|
+
TRUSTED_PROXIES = [/
|
14
|
+
\A127\.0\.0\.1\Z|
|
15
|
+
\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|
|
16
|
+
\A::1\Z|\Afd[0-9a-f]{2}:.+|
|
17
|
+
\Alocalhost\Z|
|
18
|
+
\Aunix\Z|
|
19
|
+
\Aunix:
|
20
|
+
/ix].freeze
|
12
21
|
|
13
22
|
# @note this value is not assigned as we don't recommend using a whitelist. If you need to use
|
14
23
|
# one, this constant is provided as a good default.
|
@@ -32,7 +41,7 @@ module Castle
|
|
32
41
|
].freeze
|
33
42
|
|
34
43
|
attr_accessor :host, :port, :request_timeout, :url_prefix
|
35
|
-
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy
|
44
|
+
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy, :ip_headers, :trusted_proxies
|
36
45
|
|
37
46
|
def initialize
|
38
47
|
@formatter = Castle::HeaderFormatter.new
|
@@ -43,11 +52,13 @@ module Castle
|
|
43
52
|
self.url_prefix = URL_PREFIX
|
44
53
|
self.whitelisted = [].freeze
|
45
54
|
self.blacklisted = [].freeze
|
46
|
-
self.api_secret = ''
|
55
|
+
self.api_secret = ENV.fetch('CASTLE_API_SECRET', '')
|
56
|
+
self.ip_headers = [].freeze
|
57
|
+
self.trusted_proxies = [].freeze
|
47
58
|
end
|
48
59
|
|
49
60
|
def api_secret=(value)
|
50
|
-
@api_secret =
|
61
|
+
@api_secret = value.to_s
|
51
62
|
end
|
52
63
|
|
53
64
|
def whitelisted=(value)
|
@@ -58,6 +69,22 @@ module Castle
|
|
58
69
|
@blacklisted = (value ? value.map { |header| @formatter.call(header) } : []).freeze
|
59
70
|
end
|
60
71
|
|
72
|
+
# sets ip headers
|
73
|
+
# @param value [Array<String>]
|
74
|
+
def ip_headers=(value)
|
75
|
+
raise Castle::ConfigurationError, 'ip headers must be an Array' unless value.is_a?(Array)
|
76
|
+
|
77
|
+
@ip_headers = value.map { |header| @formatter.call(header) }.freeze
|
78
|
+
end
|
79
|
+
|
80
|
+
# sets trusted proxies
|
81
|
+
# @param value [Array<String|Regexp>]
|
82
|
+
def trusted_proxies=(value)
|
83
|
+
raise Castle::ConfigurationError, 'trusted proxies must be an Array' unless value.is_a?(Array)
|
84
|
+
|
85
|
+
@trusted_proxies = value
|
86
|
+
end
|
87
|
+
|
61
88
|
def valid?
|
62
89
|
!api_secret.to_s.empty? && !host.to_s.empty? && !port.to_s.empty?
|
63
90
|
end
|
@@ -4,36 +4,54 @@ module Castle
|
|
4
4
|
module Context
|
5
5
|
class Default
|
6
6
|
def initialize(request, cookies = nil)
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
7
|
+
@pre_headers = HeaderFilter.new(request).call
|
8
|
+
@cookies = cookies || request.cookies
|
9
|
+
@request = request
|
10
10
|
end
|
11
11
|
|
12
12
|
def call
|
13
|
-
defaults.merge!(additional_defaults)
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def defaults
|
19
13
|
{
|
20
|
-
client_id:
|
14
|
+
client_id: client_id,
|
21
15
|
active: true,
|
22
16
|
origin: 'web',
|
23
|
-
headers:
|
24
|
-
ip:
|
17
|
+
headers: headers,
|
18
|
+
ip: ip,
|
25
19
|
library: {
|
26
20
|
name: 'castle-rb',
|
27
21
|
version: Castle::VERSION
|
28
22
|
}
|
29
|
-
}
|
23
|
+
}.tap do |result|
|
24
|
+
result[:locale] = locale if locale
|
25
|
+
result[:user_agent] = user_agent if user_agent
|
26
|
+
end
|
30
27
|
end
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
private
|
30
|
+
|
31
|
+
# @return [String]
|
32
|
+
def locale
|
33
|
+
@pre_headers['Accept-Language']
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
def user_agent
|
38
|
+
@pre_headers['User-Agent']
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [String]
|
42
|
+
def ip
|
43
|
+
Extractors::IP.new(@pre_headers).call
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String]
|
47
|
+
def client_id
|
48
|
+
Extractors::ClientId.new(@pre_headers, @cookies).call
|
49
|
+
end
|
50
|
+
|
51
|
+
# formatted and filtered headers
|
52
|
+
# @return [Hash]
|
53
|
+
def headers
|
54
|
+
Extractors::Headers.new(@pre_headers).call
|
37
55
|
end
|
38
56
|
end
|
39
57
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Castle
|
4
|
+
# list of events based on https://docs.castle.io/api_reference/#list-of-recognized-events
|
5
|
+
module Events
|
6
|
+
# Record when a user succesfully logs in.
|
7
|
+
LOGIN_SUCCEEDED = '$login.succeeded'
|
8
|
+
# Record when a user failed to log in.
|
9
|
+
LOGIN_FAILED = '$login.failed'
|
10
|
+
# Record when a user logs out.
|
11
|
+
LOGOUT_SUCCEEDED = '$logout.succeeded'
|
12
|
+
# Record when a user updated their profile (including password, email, phone, etc).
|
13
|
+
PROFILE_UPDATE_SUCCEEDED = '$profile_update.succeeded'
|
14
|
+
# Record errors when updating profile.
|
15
|
+
PROFILE_UPDATE_FAILED = '$profile_update.failed'
|
16
|
+
# Capture account creation, both when a user signs up as well as when created manually
|
17
|
+
# by an administrator.
|
18
|
+
REGISTRATION_SUCCEEDED = '$registration.succeeded'
|
19
|
+
# Record when an account failed to be created.
|
20
|
+
REGISTRATION_FAILED = '$registration.failed'
|
21
|
+
# The user completed all of the steps in the password reset process and the password was
|
22
|
+
# successfully reset.Password resets do not required knowledge of the current password.
|
23
|
+
PASSWORD_RESET_SUCCEEDED = '$password_reset.succeeded'
|
24
|
+
# Use to record when a user failed to reset their password.
|
25
|
+
PASSWORD_RESET_FAILED = '$password_reset.failed'
|
26
|
+
# The user successfully requested a password reset.
|
27
|
+
PASSWORD_RESET_REQUEST_SUCCCEEDED = '$password_reset_request.succeeded'
|
28
|
+
# The user failed to request a password reset.
|
29
|
+
PASSWORD_RESET_REQUEST_FAILED = '$password_reset_request.failed'
|
30
|
+
# User account has been reset.
|
31
|
+
INCIDENT_MITIGATED = '$incident.mitigated'
|
32
|
+
# User confirmed malicious activity.
|
33
|
+
REVIEW_ESCALATED = '$review.escalated'
|
34
|
+
# User confirmed safe activity.
|
35
|
+
REVIEW_RESOLVED = '$review.resolved'
|
36
|
+
# Record when a user is prompted with additional verification, such as two-factor
|
37
|
+
# authentication or a captcha.
|
38
|
+
CHALLENGE_REQUESTED = '$challenge.requested'
|
39
|
+
# Record when additional verification was successful.
|
40
|
+
CHALLENGE_SUCCEEDED = '$challenge.succeeded'
|
41
|
+
# Record when additional verification failed.
|
42
|
+
CHALLENGE_FAILED = '$challenge.failed'
|
43
|
+
# Record when a user attempts an in-app transaction, such as a purchase or withdrawal.
|
44
|
+
TRANSACTION_ATTEMPTED = '$transaction.attempted'
|
45
|
+
# Record when a user session is extended, or use any time you want
|
46
|
+
# to re-authenticate a user mid-session.
|
47
|
+
SESSION_EXTENDED = '$session.extended'
|
48
|
+
end
|
49
|
+
end
|
@@ -4,13 +4,17 @@ module Castle
|
|
4
4
|
module Extractors
|
5
5
|
# used for extraction of cookies and headers from the request
|
6
6
|
class ClientId
|
7
|
-
|
8
|
-
|
7
|
+
# @param headers [Hash]
|
8
|
+
# @param cookies [NilClass|Hash]
|
9
|
+
def initialize(headers, cookies)
|
10
|
+
@headers = headers
|
9
11
|
@cookies = cookies || {}
|
10
12
|
end
|
11
13
|
|
14
|
+
# extracts client id
|
15
|
+
# @return [String]
|
12
16
|
def call
|
13
|
-
@
|
17
|
+
@headers['X-Castle-Client-Id'] || @cookies['__cid'] || ''
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -5,47 +5,41 @@ module Castle
|
|
5
5
|
# used for extraction of cookies and headers from the request
|
6
6
|
class Headers
|
7
7
|
# Headers that we will never scrub, even if they land on the configuration blacklist.
|
8
|
-
|
8
|
+
ALWAYS_WHITELISTED = %w[User-Agent].freeze
|
9
9
|
|
10
10
|
# Headers that will always be scrubbed, even if whitelisted.
|
11
|
-
|
11
|
+
ALWAYS_BLACKLISTED = %w[Cookie Authorization].freeze
|
12
12
|
|
13
|
-
|
14
|
-
CONTENT_LENGTH = 'CONTENT_LENGTH'
|
13
|
+
private_constant :ALWAYS_WHITELISTED, :ALWAYS_BLACKLISTED
|
15
14
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
:CONTENT_LENGTH, :HTTP_HEADER_PREFIX
|
21
|
-
|
22
|
-
# @param request [Rack::Request]
|
23
|
-
def initialize(request)
|
24
|
-
@request_env = request.env
|
25
|
-
@formatter = HeaderFormatter.new
|
15
|
+
# @param headers [Hash]
|
16
|
+
def initialize(headers)
|
17
|
+
@headers = headers
|
18
|
+
@no_whitelist = Castle.config.whitelisted.empty?
|
26
19
|
end
|
27
20
|
|
28
21
|
# Serialize HTTP headers
|
29
22
|
# @return [Hash]
|
30
23
|
def call
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
header = @formatter.call(env_header)
|
35
|
-
|
36
|
-
if ALWAYS_SCRUBBED_HEADERS.include?(header)
|
37
|
-
acc[header] = true
|
38
|
-
elsif ALWAYS_INCLUDED_HEADERS.include?(header)
|
39
|
-
acc[header] = @request_env[env_header]
|
40
|
-
elsif Castle.config.blacklisted.include?(header)
|
41
|
-
acc[header] = true
|
42
|
-
elsif Castle.config.whitelisted.empty? || Castle.config.whitelisted.include?(header)
|
43
|
-
acc[header] = @request_env[env_header]
|
44
|
-
else
|
45
|
-
acc[header] = true
|
46
|
-
end
|
24
|
+
@headers.each_with_object({}) do |(name, value), acc|
|
25
|
+
acc[name] = header_value(name, value)
|
47
26
|
end
|
48
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# scrub header value
|
32
|
+
# @param name [String]
|
33
|
+
# @param value [String]
|
34
|
+
# @return [TrueClass | FalseClass | String]
|
35
|
+
def header_value(name, value)
|
36
|
+
return true if ALWAYS_BLACKLISTED.include?(name)
|
37
|
+
return value if ALWAYS_WHITELISTED.include?(name)
|
38
|
+
return true if Castle.config.blacklisted.include?(name)
|
39
|
+
return value if @no_whitelist || Castle.config.whitelisted.include?(name)
|
40
|
+
|
41
|
+
true
|
42
|
+
end
|
49
43
|
end
|
50
44
|
end
|
51
45
|
end
|