right_support 2.11.3 → 2.12.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/Rakefile +4 -0
- data/VERSION +1 -1
- data/lib/right_support/notifiers/airbrake.rb +194 -0
- data/lib/right_support/notifiers/base.rb +73 -0
- data/lib/right_support/notifiers/blacklisters/base.rb +48 -0
- data/lib/right_support/notifiers/blacklisters/canonical.rb +60 -0
- data/lib/right_support/notifiers/blacklisters/regular_expression.rb +86 -0
- data/{features/support/file_utils_bundler_mixin.rb → lib/right_support/notifiers/blacklisters/simple.rb} +21 -20
- data/lib/right_support/notifiers/blacklisters/snake_case.rb +60 -0
- data/lib/right_support/notifiers/blacklisters/wildcard.rb +65 -0
- data/lib/right_support/notifiers/blacklisters.rb +34 -0
- data/lib/right_support/notifiers/logger.rb +94 -0
- data/lib/right_support/notifiers/notification.rb +57 -0
- data/lib/right_support/notifiers/utilities/backtrace_decoder.rb +234 -0
- data/lib/right_support/notifiers/utilities.rb +29 -0
- data/lib/right_support/notifiers.rb +32 -0
- data/lib/right_support/rack/request_logger.rb +13 -9
- data/lib/right_support.rb +1 -0
- data/right_support.gemspec +19 -70
- metadata +17 -69
- data/.coveralls.yml +0 -2
- data/.rspec +0 -3
- data/.simplecov +0 -6
- data/.travis.yml +0 -13
- data/Gemfile +0 -51
- data/Gemfile.lock +0 -153
- data/features/balancer_error_handling.feature +0 -34
- data/features/balancer_health_check.feature +0 -33
- data/features/hash_tools.feature +0 -27
- data/features/http_client_timeout.feature +0 -19
- data/features/serialization.feature +0 -113
- data/features/step_definitions/hash_tools_steps.rb +0 -41
- data/features/step_definitions/http_client_steps.rb +0 -27
- data/features/step_definitions/request_balancer_steps.rb +0 -93
- data/features/step_definitions/ruby_steps.rb +0 -176
- data/features/step_definitions/serialization_steps.rb +0 -133
- data/features/step_definitions/server_steps.rb +0 -134
- data/features/support/env.rb +0 -148
- data/right_support.rconf +0 -9
- data/spec/config/feature_set_spec.rb +0 -83
- data/spec/crypto/signed_hash_spec.rb +0 -73
- data/spec/data/hash_tools_spec.rb +0 -602
- data/spec/data/mash_spec.rb +0 -313
- data/spec/data/token_spec.rb +0 -21
- data/spec/data/uuid_spec.rb +0 -45
- data/spec/db/cassandra_model_part1_spec.rb +0 -84
- data/spec/db/cassandra_model_part2_spec.rb +0 -73
- data/spec/db/cassandra_model_spec.rb +0 -375
- data/spec/fixtures/encrypted_priv_rsa.pem +0 -30
- data/spec/fixtures/good_priv_dsa.pem +0 -12
- data/spec/fixtures/good_priv_rsa.pem +0 -15
- data/spec/fixtures/good_pub_dsa.ssh +0 -1
- data/spec/fixtures/good_pub_rsa.pem +0 -5
- data/spec/fixtures/good_pub_rsa.ssh +0 -1
- data/spec/log/exception_logger_spec.rb +0 -76
- data/spec/log/filter_logger_spec.rb +0 -66
- data/spec/log/mixin_spec.rb +0 -141
- data/spec/log/multiplexer_spec.rb +0 -54
- data/spec/log/null_logger_spec.rb +0 -36
- data/spec/log/step_level_logger_spec.rb +0 -49
- data/spec/log/system_logger_spec.rb +0 -172
- data/spec/net/address_helper_spec.rb +0 -57
- data/spec/net/dns_spec.rb +0 -187
- data/spec/net/http_client_spec.rb +0 -181
- data/spec/net/lb/health_check_spec.rb +0 -417
- data/spec/net/lb/round_robin_spec.rb +0 -15
- data/spec/net/lb/sticky_spec.rb +0 -92
- data/spec/net/request_balancer_spec.rb +0 -690
- data/spec/net/s3_helper_spec.rb +0 -160
- data/spec/net/ssl_spec.rb +0 -42
- data/spec/net/string_encoder_spec.rb +0 -58
- data/spec/rack/log_setter_spec.rb +0 -5
- data/spec/rack/request_logger_spec.rb +0 -225
- data/spec/rack/request_tracker_spec.rb +0 -115
- data/spec/rack/runtime_spec.rb +0 -49
- data/spec/ruby/easy_singleton_spec.rb +0 -72
- data/spec/ruby/object_extensions_spec.rb +0 -27
- data/spec/ruby/string_extensions_spec.rb +0 -98
- data/spec/spec_helper.rb +0 -188
- data/spec/stats/activity_spec.rb +0 -425
- data/spec/stats/exceptions_spec.rb +0 -247
- data/spec/stats/helpers_spec.rb +0 -685
- data/spec/validation/openssl_spec.rb +0 -37
- data/spec/validation/ssh_spec.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7074bbc2d6d01fc7eb2bb07e1feeef2ba2f53ed
|
4
|
+
data.tar.gz: 95c557ae302fce0f8612aa6db52f5c4741bf8748
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffb1c16256704d39e72a2661d8a3fb4c7cda875cc3de50eab7d6825ae42cb4b178c3ca01110d28d6655bc0d40674478e6b6565f605cd045c56ca438cb993f1f0
|
7
|
+
data.tar.gz: 62082378411fc6876b5d76f06bc8ec1a48960521ef4221e24aee9b6e187fa2e2df394364fb58b34cdc317a5ada5b7fa433c69b13c6670752d3e2caf8cf27ca04
|
data/Rakefile
CHANGED
@@ -62,6 +62,10 @@ if defined?(Jeweler)
|
|
62
62
|
gem.description = %Q{A toolkit of useful, reusable foundation code created by RightScale.}
|
63
63
|
gem.email = "support@rightscale.com"
|
64
64
|
gem.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson', 'Lee Kirchhoff', 'Alexey Karpik', 'Scott Messier']
|
65
|
+
gem.files.exclude "Gemfile*"
|
66
|
+
gem.files.exclude ".*"
|
67
|
+
gem.files.exclude "features/**/*"
|
68
|
+
gem.files.exclude "spec/**/*"
|
65
69
|
end
|
66
70
|
|
67
71
|
# Never auto-commit during operations that change the repository. Allows developers to decide on their own commit comment
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.12.1
|
@@ -0,0 +1,194 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
if require_succeeds?('airbrake-ruby')
|
24
|
+
|
25
|
+
# monkey-patch airbrake's backtrace parser since we parse it with more options
|
26
|
+
# in ::RightSupport::Notifier::Utilities::BacktraceDecoder
|
27
|
+
require 'airbrake-ruby/backtrace'
|
28
|
+
|
29
|
+
module Airbrake
|
30
|
+
module Backtrace
|
31
|
+
class << self
|
32
|
+
alias :old_parse :parse
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.backtrace_decoder=(value)
|
36
|
+
@backtrace_decoder = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.parse(*args)
|
40
|
+
if @backtrace_decoder
|
41
|
+
error = args.first
|
42
|
+
trace = error.backtrace || []
|
43
|
+
@backtrace_decoder.decode(trace).map do |frame|
|
44
|
+
{
|
45
|
+
file: frame.file,
|
46
|
+
line: frame.line,
|
47
|
+
function: frame.function
|
48
|
+
}
|
49
|
+
end
|
50
|
+
else
|
51
|
+
old_parse(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class RightSupport::Notifier::Airbrake < RightSupport::Notifier::Base
|
58
|
+
|
59
|
+
# Rack environment keys that are safe to send to Errbit, et al.
|
60
|
+
SAFE_ENVIRONMENT_KEYS = /^(?:HTTP_|REMOTE_|REQUEST_|SERVER_|QUERY_)/
|
61
|
+
|
62
|
+
# default blacklisted request headers.
|
63
|
+
DEFAULT_ENVIRONMENT_BLACKLIST = %w(
|
64
|
+
Authorization Cookie
|
65
|
+
Proxy-Authorization
|
66
|
+
X-Api-Shared-Secret
|
67
|
+
X-Authorization X-Http-Authorization
|
68
|
+
)
|
69
|
+
|
70
|
+
# default blacklisted payload
|
71
|
+
DEFAULT_PAYLOAD_BLACKLIST = %w(password)
|
72
|
+
|
73
|
+
# @param [String] api_key required API key (a.k.a project key in paid service)
|
74
|
+
# @param [Hash] options
|
75
|
+
# @option options [String] :endpoint optional for hosted freeware errbit,
|
76
|
+
# defaults to paid airbrake service.
|
77
|
+
# @option options [Integer] :project_id ignored by the freeware errbit,
|
78
|
+
# required to use the paid airbrake service.
|
79
|
+
# @option options [Array|String] :environment_blacklist header names to
|
80
|
+
# obfuscate after merging with default
|
81
|
+
# ['Authorization', 'Cookie', 'X-Api-Shared-Secret'] or nil or empty
|
82
|
+
# @option options [Blacklister] :environment_blacklister optional custom
|
83
|
+
# environment blacklister. supercedes :environment_blacklist
|
84
|
+
# @option options [Array|String] :payload_blacklist top-level payload keys
|
85
|
+
# to obfuscate after merging with default ['password'] or nil or empty
|
86
|
+
# @option options [Blacklister] :payload_blacklister optional custom payload
|
87
|
+
# blacklister. supercedes :payload_blacklist
|
88
|
+
# @option options [String] :root_directory as application root directory.
|
89
|
+
# default=<working directory>
|
90
|
+
# @option options [String] :environment as deployment environment.
|
91
|
+
# default=<RACK_ENV>
|
92
|
+
# @option options [Integer] :backtrace_limit as maximum number of stack frames
|
93
|
+
# to decode or negative for all. default = 10.
|
94
|
+
# @option options [String|Array] :path_blacklist for unwanted paths in
|
95
|
+
# backtrace. the blacklisted path substring causes the frame to be omitted
|
96
|
+
# when it appears anywhere in the traced path. default = none.
|
97
|
+
# @option options [Proc] :notifiable_callback optional callback to determine
|
98
|
+
# if a particular error is worthy of notification. takes an error parameter
|
99
|
+
# and returns true if notifiable, false if ignored.
|
100
|
+
# @option options [Logger] :logger or nil for default
|
101
|
+
def initialize(api_key, options)
|
102
|
+
super(options)
|
103
|
+
options = {
|
104
|
+
endpoint: nil,
|
105
|
+
project_id: nil,
|
106
|
+
environment_blacklist: nil,
|
107
|
+
environment_blacklister: nil,
|
108
|
+
payload_blacklist: nil,
|
109
|
+
payload_blacklister: nil,
|
110
|
+
root_directory: ::Dir.pwd,
|
111
|
+
environment: ::ENV['RACK_ENV'] || 'development'
|
112
|
+
}.merge(options)
|
113
|
+
|
114
|
+
# resolve environment blacklist.
|
115
|
+
unless @environment_blacklister = options[:environment_blacklister]
|
116
|
+
environment_blacklist = (options[:environment_blacklist] || []) + DEFAULT_ENVIRONMENT_BLACKLIST
|
117
|
+
environment_blacklist.map! do |eb|
|
118
|
+
# prefix environment key with 'HTTP_' for a request header match unless
|
119
|
+
# prefix is already known.
|
120
|
+
if SAFE_ENVIRONMENT_KEYS.match(eb)
|
121
|
+
eb
|
122
|
+
else
|
123
|
+
'HTTP_' + eb
|
124
|
+
end
|
125
|
+
end
|
126
|
+
@environment_blacklister = ::RightSupport::Notifier::Blacklister::Canonical.new(environment_blacklist)
|
127
|
+
end
|
128
|
+
|
129
|
+
# resolve payload blacklist.
|
130
|
+
unless @payload_blacklister = options[:payload_blacklister]
|
131
|
+
payload_blacklist = (options[:payload_blacklist] || []) + DEFAULT_PAYLOAD_BLACKLIST
|
132
|
+
@payload_blacklister = ::RightSupport::Notifier::Blacklister::SnakeCase.new(payload_blacklist)
|
133
|
+
end
|
134
|
+
|
135
|
+
::Airbrake::Backtrace.backtrace_decoder = backtrace_decoder
|
136
|
+
::Airbrake.configure do |config|
|
137
|
+
config.host = options[:endpoint] if options[:endpoint] # defaults to paid airbrake service
|
138
|
+
config.project_key = api_key
|
139
|
+
config.project_id = Integer(options[:project_id] || 1) # ignored by freeware errbit service but has type integer in gobrake, etc.
|
140
|
+
config.root_directory = options[:root_directory]
|
141
|
+
config.environment = options[:environment]
|
142
|
+
config.queue_size = 100
|
143
|
+
config.workers = 5
|
144
|
+
config.logger = options[:logger] || ::RightSupport::Log::Mixin.default_logger
|
145
|
+
end
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
def notify(notification)
|
150
|
+
# RACK environment key whitelisting.
|
151
|
+
env = notification.env.select { |k, v| k =~ SAFE_ENVIRONMENT_KEYS }
|
152
|
+
|
153
|
+
# environment (request headers, etc.) blacklisting.
|
154
|
+
@environment_blacklister.filter(env)
|
155
|
+
|
156
|
+
# always insert error token.
|
157
|
+
env['error.token'] = notification.error_token
|
158
|
+
|
159
|
+
# payload blacklisting.
|
160
|
+
if payload = notification.payload
|
161
|
+
# deep mash payload to avoid modifying original and to normalize keys.
|
162
|
+
# also note that the gem has an Airbrake::PayloadTruncator used to
|
163
|
+
# abbreviate the payload before sending so do not expect to always see
|
164
|
+
# the full detail on the website.
|
165
|
+
payload = ::RightSupport::Data::HashTools.deep_mash(payload)
|
166
|
+
@payload_blacklister.filter(payload)
|
167
|
+
end
|
168
|
+
|
169
|
+
# note we are only interested in the root cause for airbrake purposes.
|
170
|
+
# the airbrake gem will iterate over .cause but there is no need to send
|
171
|
+
# the entire cause chain in the notification. full details appear in the
|
172
|
+
# logger notifier when DEBUG_MODE=true
|
173
|
+
cause, _ = backtrace_decoder.walk_error(notification.error, raw_trace: true)
|
174
|
+
notice = ::Airbrake.build_notice(
|
175
|
+
cause,
|
176
|
+
component: notification.component,
|
177
|
+
action: notification.action)
|
178
|
+
notice[:params] = payload
|
179
|
+
notice[:session] = notification.global_session
|
180
|
+
notice[:environment] = env
|
181
|
+
|
182
|
+
# notify
|
183
|
+
::Airbrake.notify(notice)
|
184
|
+
true
|
185
|
+
rescue ::Exception => e
|
186
|
+
lines = [e.class.name, e.message, e.backtrace].flatten.compact
|
187
|
+
msg = "Failed to notify: #{lines.join("\n")}"
|
188
|
+
logger.error(msg)
|
189
|
+
true
|
190
|
+
end
|
191
|
+
|
192
|
+
end # RightSupport::Notifier::Airbrake
|
193
|
+
|
194
|
+
end # require_succeeds?('airbrake-ruby')
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
# base class for notifiers.
|
24
|
+
class RightSupport::Notifier::Base
|
25
|
+
include ::RightSupport::Log::Mixin
|
26
|
+
|
27
|
+
attr_reader :backtrace_decoder
|
28
|
+
|
29
|
+
# @param [Hash] options
|
30
|
+
# @option options [Integer] :backtrace_limit as maximum number of stack frames
|
31
|
+
# to decode or negative for all. default = 10.
|
32
|
+
# @option options [String|Array] :path_blacklist for unwanted paths in
|
33
|
+
# backtrace. the blacklisted path substring causes the frame to be omitted
|
34
|
+
# when it appears anywhere in the traced path. default = none.
|
35
|
+
# @option options [Proc] :notifiable_callback optional callback to determine
|
36
|
+
# if a particular error is worthy of notification. takes an error parameter
|
37
|
+
# and returns true if notifiable, false if ignored.
|
38
|
+
# @option options [Logger] :logger or nil for default
|
39
|
+
def initialize(options)
|
40
|
+
options = {
|
41
|
+
backtrace_limit: 10,
|
42
|
+
notifiable_callback: nil,
|
43
|
+
logger: nil
|
44
|
+
}.merge(options)
|
45
|
+
@notifiable_callback = options[:notifiable_callback]
|
46
|
+
@logger = options[:logger]
|
47
|
+
@backtrace_decoder = ::RightSupport::Notifier::Utility::BacktraceDecoder.new(
|
48
|
+
backtrace_limit: Integer(options[:backtrace_limit]),
|
49
|
+
path_blacklist: Array(options[:path_blacklist]))
|
50
|
+
@debug_mode = ::ENV['DEBUG_MODE'] == 'true'
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [TrueClass|FalseClass] true in debug mode
|
54
|
+
def debug_mode?
|
55
|
+
@debug_mode
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [TrueClass|FalseClass] true if the error is notifiable for the
|
59
|
+
# current notifier, false to ignore error.
|
60
|
+
def notifiable?(e)
|
61
|
+
@notifiable_callback.nil? || @notifiable_callback.call(e)
|
62
|
+
end
|
63
|
+
|
64
|
+
# performs a notification.
|
65
|
+
#
|
66
|
+
# @param [RightSupport::Notifier::Notification] notification to perform.
|
67
|
+
#
|
68
|
+
# @return [TrueClass] always true
|
69
|
+
def notify(notification)
|
70
|
+
fail 'Must be overridden'
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
# base class for a partial data blacklister.
|
24
|
+
class RightSupport::Notifier::Blacklister::Base
|
25
|
+
|
26
|
+
attr_reader :replacement_value
|
27
|
+
|
28
|
+
# @param [Hash] options
|
29
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
30
|
+
# default='HIDDEN'.
|
31
|
+
def initialize(options)
|
32
|
+
options = {
|
33
|
+
replacement_value: 'HIDDEN'
|
34
|
+
}.merge(options)
|
35
|
+
@replacement_value = options[:replacement_value]
|
36
|
+
end
|
37
|
+
|
38
|
+
# filters data by finding and replacing blacklisted key patterns with a
|
39
|
+
# replacement value.
|
40
|
+
#
|
41
|
+
# @param [Hash] data for filtering by blacklist
|
42
|
+
#
|
43
|
+
# @return [TrueClass] always true
|
44
|
+
def filter(data)
|
45
|
+
fail 'Must be overridden'
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'set'
|
24
|
+
|
25
|
+
# implements a blacklister that matches canonical keys. a canonical key follows
|
26
|
+
# the request/response header naming pattern(s) and/or can be converted to one
|
27
|
+
# of these naming patterns.
|
28
|
+
class RightSupport::Notifier::Blacklister::Canonical < ::RightSupport::Notifier::Blacklister::Base
|
29
|
+
|
30
|
+
# @param [Array|String] keys to blacklist
|
31
|
+
# @param [Hash] options
|
32
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
33
|
+
# default='HIDDEN'.
|
34
|
+
def initialize(keys, options = {})
|
35
|
+
super(options)
|
36
|
+
@keys = ::RightSupport::Notifier::Blacklister::Canonical.normalize_keys(keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
# implements RightSupport::Notifier::Blacklister::Base#filter
|
40
|
+
def filter(data)
|
41
|
+
data.keys.each do |key|
|
42
|
+
if @keys.include?(self.class.to_canonical(key))
|
43
|
+
data[key] = replacement_value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [Array|String] keys to normalize
|
50
|
+
def self.normalize_keys(keys)
|
51
|
+
::Set.new(Array(keys).map(&self.method(:to_canonical)))
|
52
|
+
end
|
53
|
+
|
54
|
+
# canonicalizes a key (header name) for easy lookup in a Rack env hash.
|
55
|
+
#
|
56
|
+
# @param [String|Symbol] key to canonicalize
|
57
|
+
def self.to_canonical(key)
|
58
|
+
key.to_s.upcase.gsub('-', '_')
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'set'
|
24
|
+
|
25
|
+
# implements a blacklister that matches key paths by regular expression.
|
26
|
+
class RightSupport::Notifier::Blacklister::RegularExpression < ::RightSupport::Notifier::Blacklister::Base
|
27
|
+
|
28
|
+
attr_reader :delimiter
|
29
|
+
|
30
|
+
# @param [Array|Regexp|String] regular_expressions to blacklist
|
31
|
+
# @param [Hash] options
|
32
|
+
# @option options [Object] :delimiter present in regular expressions used to
|
33
|
+
# distinguish child data maps. defaults to slash (/).
|
34
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
35
|
+
# default='HIDDEN'.
|
36
|
+
def initialize(regular_expressions, options = {})
|
37
|
+
super(options)
|
38
|
+
options = {
|
39
|
+
delimiter: '/'
|
40
|
+
}.merge(options)
|
41
|
+
@delimiter = options[:delimiter].to_s
|
42
|
+
@regular_expressions = ::RightSupport::Notifier::Blacklister::RegularExpression.normalize_regular_expressions(regular_expressions)
|
43
|
+
end
|
44
|
+
|
45
|
+
# implements RightSupport::Notifier::Blacklister::Base#filter
|
46
|
+
def filter(data)
|
47
|
+
recursive_filter(data, nil)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [Array|Regexp|String] regular_expressions to normalize
|
51
|
+
def self.normalize_regular_expressions(regular_expressions)
|
52
|
+
::Set.new(Array(regular_expressions).map(&self.method(:to_regexp)))
|
53
|
+
end
|
54
|
+
|
55
|
+
# compiles a regular expression, if necessary.
|
56
|
+
#
|
57
|
+
# @param [String|Regexp] re to compile or accept
|
58
|
+
#
|
59
|
+
# @return [Regexp] regular expression
|
60
|
+
def self.to_regexp(re)
|
61
|
+
case re
|
62
|
+
when ::String
|
63
|
+
::Regexp.compile(re)
|
64
|
+
when ::Regexp
|
65
|
+
re
|
66
|
+
else
|
67
|
+
fail "Unexpected type: #{re.class.name}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def recursive_filter(data, parent_path)
|
74
|
+
data.keys.each do |k|
|
75
|
+
unless (v = data[k]).nil?
|
76
|
+
current_path = parent_path ? "#{parent_path}#{delimiter}#{k}" : k.to_s
|
77
|
+
if @regular_expressions.any? { |re| re.match(current_path) }
|
78
|
+
data[k] = replacement_value
|
79
|
+
elsif v.respond_to?(:has_key?)
|
80
|
+
recursive_filter(v, current_path)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
true
|
85
|
+
end
|
86
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
2
3
|
#
|
3
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
5
|
# a copy of this software and associated documentation files (the
|
@@ -19,27 +20,27 @@
|
|
19
20
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
23
|
+
require 'set'
|
24
|
+
|
25
|
+
# implements a simple blacklister that matches a literal top-level key.
|
26
|
+
class RightSupport::Notifier::Blacklister::Simple < ::RightSupport::Notifier::Blacklister::Base
|
27
|
+
|
28
|
+
# @param [Array|String] keys to blacklist
|
29
|
+
# @param [Hash] options
|
30
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
31
|
+
# default='HIDDEN'.
|
32
|
+
def initialize(keys, options = {})
|
33
|
+
super(options)
|
34
|
+
@keys = ::Set.new(Array(keys).map(&:to_s))
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
# implements RightSupport::Notifier::Blacklister::Base#filter
|
38
|
+
def filter(data)
|
39
|
+
@keys.each do |key|
|
40
|
+
if data.has_key?(key)
|
41
|
+
data[key] = replacement_value
|
42
|
+
end
|
40
43
|
end
|
44
|
+
true
|
41
45
|
end
|
42
46
|
end
|
43
|
-
|
44
|
-
# Install the magic hook.
|
45
|
-
Kernel.instance_eval { include(::FileUtilsBundlerMixin) }
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'set'
|
24
|
+
|
25
|
+
# implements a blacklister that matches snake_case keys.
|
26
|
+
class RightSupport::Notifier::Blacklister::SnakeCase < ::RightSupport::Notifier::Blacklister::Base
|
27
|
+
|
28
|
+
# @param [Array|String] keys to blacklist
|
29
|
+
# @param [Hash] options
|
30
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
31
|
+
# default='HIDDEN'.
|
32
|
+
def initialize(keys, options = {})
|
33
|
+
super(options)
|
34
|
+
@keys = ::RightSupport::Notifier::Blacklister::SnakeCase.normalize_keys(keys)
|
35
|
+
end
|
36
|
+
|
37
|
+
# implements RightSupport::Notifier::Blacklister::Base#filter
|
38
|
+
def filter(data)
|
39
|
+
data.keys.each do |key|
|
40
|
+
if @keys.include?(self.class.to_snake_case(key))
|
41
|
+
data[key] = replacement_value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [Array|String] keys to normalize
|
48
|
+
def self.normalize_keys(keys)
|
49
|
+
::Set.new(Array(keys).map(&self.method(:to_snake_case)))
|
50
|
+
end
|
51
|
+
|
52
|
+
# converts a key (field name) to snake_case
|
53
|
+
def self.to_snake_case(key)
|
54
|
+
key.to_s.
|
55
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
56
|
+
gsub(/([a-z])([A-Z])/, '\1_\2').
|
57
|
+
downcase
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
# implements a blacklister that matches key paths by wildcard pattern
|
24
|
+
# interpreted as a key/value hierarchy represented as
|
25
|
+
# slash (/) delimited parts of the pattern string. supports the usual [*,?]
|
26
|
+
# wildcard characters as well as the more advanced multiple-indirection "**/"
|
27
|
+
# pattern.
|
28
|
+
#
|
29
|
+
# example:
|
30
|
+
# patterns = ['a/*/c']
|
31
|
+
# data = {a: {b: c: 42}}
|
32
|
+
# result == {a: {b: c: 'HIDDEN'}}
|
33
|
+
class RightSupport::Notifier::Blacklister::Wildcard < ::RightSupport::Notifier::Blacklister::RegularExpression
|
34
|
+
|
35
|
+
# @param [Array|String] patterns to blacklist
|
36
|
+
# @param [Hash] options
|
37
|
+
# @option options [Object] :replacement_value of any kind including nil.
|
38
|
+
# default='HIDDEN'.
|
39
|
+
def initialize(patterns, options = {})
|
40
|
+
if options[:delimiter]
|
41
|
+
raise ::ArgumentError, 'options[:delimiter] is not allowed for wildcards.'
|
42
|
+
end
|
43
|
+
super(
|
44
|
+
::RightSupport::Notifier::Blacklister::Wildcard.to_regular_expressions(Array(patterns)),
|
45
|
+
options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.to_regular_expressions(patterns)
|
49
|
+
patterns.map do |pattern|
|
50
|
+
# remove leading/trailing slashes
|
51
|
+
here = pattern == 'a/b/*'
|
52
|
+
pattern = pattern.to_s.gsub(/^[\/]+(.*)/, '\2').gsub(/(.*)[\/]+$/, '\1')
|
53
|
+
|
54
|
+
# remove trailing "/*" as matching the parent will obfuscate all children.
|
55
|
+
while pattern.end_with?('/*')
|
56
|
+
pattern = pattern[0...-2]
|
57
|
+
end
|
58
|
+
pattern = ::Regexp.escape(pattern) # escape all
|
59
|
+
pattern = pattern.gsub('\?', '[^/]') # question mark == match any single non-slash character
|
60
|
+
pattern = pattern.gsub('\*\*/', '.*/') # '**/' == match at any depth
|
61
|
+
pattern = pattern.gsub('\*', '[^/]*') # asterisk == match zero or more non-slash characters
|
62
|
+
::Regexp.compile(pattern)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|