lenjador 1.2.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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +8 -0
- data/README.md +121 -0
- data/Rakefile +7 -0
- data/benchmark/whitelisting.rb +53 -0
- data/lenjador.gemspec +36 -0
- data/lib/lenjador/adapters/stdout_adapter.rb +29 -0
- data/lib/lenjador/adapters/stdout_json_adapter.rb +60 -0
- data/lib/lenjador/adapters.rb +22 -0
- data/lib/lenjador/null_logger.rb +18 -0
- data/lib/lenjador/preprocessors/blacklist.rb +61 -0
- data/lib/lenjador/preprocessors/json_pointer_trie.rb +40 -0
- data/lib/lenjador/preprocessors/strategies/mask.rb +43 -0
- data/lib/lenjador/preprocessors/strategies/prune.rb +44 -0
- data/lib/lenjador/preprocessors/whitelist.rb +56 -0
- data/lib/lenjador/preprocessors.rb +18 -0
- data/lib/lenjador/utils.rb +91 -0
- data/lib/lenjador.rb +74 -0
- data/spec/adapters/stdout_adapter_spec.rb +48 -0
- data/spec/adapters/stdout_json_adapter_spec.rb +62 -0
- data/spec/lenjador_spec.rb +142 -0
- data/spec/preprocessors/blacklist_spec.rb +131 -0
- data/spec/preprocessors/json_pointer_trie.rb +36 -0
- data/spec/preprocessors/whitelist_spec.rb +335 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/implement_interface.rb +11 -0
- data/spec/utils_spec.rb +84 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0b22b9348f98f915eaa67dec7cbbe762d09f86dc
|
4
|
+
data.tar.gz: 2e22ff6de0251d2e007b83e626e14c20d860b281
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e48ff7741f76c07e5dd00e910e32cd6013ee4ff6854ccfc29bf4dc249e54635500c4ff5fedd51ac7394a643baa4a6e1a60269b41b9c5fb2006110fefe45f13af
|
7
|
+
data.tar.gz: 0cd79d4f685890c86d4c0c182f256f35c0dbe48a95b77a548193cbec05f21a8df5ce551c1fd77fe5a9a751a5a9491b04c24e78392c9bb7ae7fa92ea2f272ae29
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
logasm
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.4.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
Lenjador
|
2
|
+
================
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
|
6
|
+
### Creating a new Lenjador logger in Ruby
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
Lenjador.build(application_name, logger_config)
|
10
|
+
```
|
11
|
+
|
12
|
+
<b>logger_config</b> is a hash with logger types and their configuration.
|
13
|
+
|
14
|
+
#### Configuration
|
15
|
+
|
16
|
+
```
|
17
|
+
loggers:
|
18
|
+
stdout:
|
19
|
+
level: 'debug'
|
20
|
+
```
|
21
|
+
Supported log levels:
|
22
|
+
|
23
|
+
1. fatal
|
24
|
+
2. error
|
25
|
+
3. warn
|
26
|
+
4. info
|
27
|
+
5. debug
|
28
|
+
|
29
|
+
For example level: 'warn' will log everything with warn and above.
|
30
|
+
|
31
|
+
#### Examples
|
32
|
+
|
33
|
+
Creating a new stdout logger
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'lenjador'
|
37
|
+
|
38
|
+
lenjador = Lenjador.build('myApp', stdout: nil)
|
39
|
+
```
|
40
|
+
|
41
|
+
When no loggers are specified, it creates a stdout logger by default.
|
42
|
+
|
43
|
+
## Preprocessors
|
44
|
+
|
45
|
+
Preprocessors allow modification of log messages, prior to sending of the message to the configured logger(s).
|
46
|
+
|
47
|
+
### Blacklist
|
48
|
+
|
49
|
+
Excludes or masks defined fields of the passed hash object.
|
50
|
+
You can specify the name of the field and which action to take on it.
|
51
|
+
Nested hashes of any level are preprocessed as well.
|
52
|
+
|
53
|
+
Available actions:
|
54
|
+
|
55
|
+
* `prune` (default) - fully excludes the field and its value from the hash.
|
56
|
+
* `mask` - replaces every character from the original value with `*`.
|
57
|
+
In case of `array`, `hash` or `boolean` value is replaced with one `*`.
|
58
|
+
|
59
|
+
#### Configuration
|
60
|
+
|
61
|
+
```yaml
|
62
|
+
preprocessors:
|
63
|
+
blacklist:
|
64
|
+
fields:
|
65
|
+
- key: password
|
66
|
+
- key: phone
|
67
|
+
action: mask
|
68
|
+
```
|
69
|
+
|
70
|
+
#### Usage
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
logger = Lenjador.build(application_name, logger_config, preprocessors)
|
74
|
+
|
75
|
+
input = {password: 'password', info: {phone: '+12055555555'}}
|
76
|
+
|
77
|
+
logger.debug("Received request", input)
|
78
|
+
```
|
79
|
+
|
80
|
+
Logger output:
|
81
|
+
|
82
|
+
```
|
83
|
+
Received request {"info":{"phone":"************"}}
|
84
|
+
```
|
85
|
+
|
86
|
+
### Whitelist
|
87
|
+
|
88
|
+
Prunes or masks all the fields except those whitelisted in the configuration using [JSON Pointer](https://tools.ietf.org/html/rfc6901).
|
89
|
+
Only simple values(`string`, `number`, `boolean`) can be whitelisted.
|
90
|
+
Whitelisting array and hash elements can be done using wildcard symbol `~`.
|
91
|
+
|
92
|
+
Available actions:
|
93
|
+
|
94
|
+
* `mask` (default) - replaces every character from the original value with `*`.
|
95
|
+
In case of `array`, `hash` or `boolean` value is replaced with one `*`.
|
96
|
+
* `prune` - fully excludes the field and its value from the hash.
|
97
|
+
|
98
|
+
#### Configuration
|
99
|
+
|
100
|
+
```yaml
|
101
|
+
preprocessors:
|
102
|
+
whitelist:
|
103
|
+
pointers: ['/info/phone', '/addresses/~/host']
|
104
|
+
action: prune
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Usage
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
logger = Lenjador.build(application_name, logger_config, preprocessors)
|
111
|
+
|
112
|
+
input = {password: 'password', info: {phone: '+12055555555'}, addresses: [{host: 'example.com', path: 'info'}]}
|
113
|
+
|
114
|
+
logger.debug("Received request", input)
|
115
|
+
```
|
116
|
+
|
117
|
+
Logger output:
|
118
|
+
|
119
|
+
```
|
120
|
+
Received request {password: "********", "info": {"phone": "+12055555555"}, "addresses": [{"host": "example.com","path": "****"}]}
|
121
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'lenjador/preprocessors/whitelist'
|
3
|
+
require 'benchmark/ips'
|
4
|
+
|
5
|
+
pointers = %w[
|
6
|
+
/scalar
|
7
|
+
/flat_hash/~
|
8
|
+
/nested_hash/~/deep_hash/~
|
9
|
+
/flat_array/~
|
10
|
+
/nested_array/~/deep_array/~
|
11
|
+
]
|
12
|
+
|
13
|
+
%w[prune mask].each do |action|
|
14
|
+
preprocessor = Lenjador::Preprocessors::Whitelist.new(pointers: pointers, action: action)
|
15
|
+
|
16
|
+
Benchmark.ips do |x|
|
17
|
+
x.config(time: 5, warmup: 2)
|
18
|
+
|
19
|
+
x.report("Scalar value whitelisting (action=#{action})") do
|
20
|
+
preprocessor.process(scalar: 'value', bad_scalar: 'value', hash: {})
|
21
|
+
end
|
22
|
+
|
23
|
+
x.report("Flat hash whitelisting (action=#{action})") do
|
24
|
+
preprocessor.process(flat_hash: { scalar: 'value', array: [1, 2], hash: {} })
|
25
|
+
end
|
26
|
+
|
27
|
+
x.report("Nested hash whitelisting (action=#{action})") do
|
28
|
+
preprocessor.process(
|
29
|
+
nested_hash: {
|
30
|
+
next_level_hash: {
|
31
|
+
deep_hash: { scalar: 'value', array: [1, 2] }
|
32
|
+
},
|
33
|
+
next_level_hash2: {
|
34
|
+
deep_hash: { scalar: 'value', array: [1, 2] }
|
35
|
+
},
|
36
|
+
next_level_hash3: {
|
37
|
+
deep_hash: { scalar: 'value', array: [1, 2] }
|
38
|
+
}
|
39
|
+
}
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
x.report("Flat array whitelisting (action=#{action})") do
|
44
|
+
preprocessor.process(
|
45
|
+
nested_array: [
|
46
|
+
{ deep_array: [1, 2, 3] },
|
47
|
+
{ deep_array: [1, 2, 3] },
|
48
|
+
{ deep_array: [1, 2, 3] }
|
49
|
+
]
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lenjador.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
if RUBY_PLATFORM =~ /java/
|
7
|
+
gem.name = 'lenjador-jruby'
|
8
|
+
else
|
9
|
+
gem.name = 'lenjador'
|
10
|
+
end
|
11
|
+
|
12
|
+
gem.version = '1.2.1'
|
13
|
+
gem.authors = ["Salemove"]
|
14
|
+
gem.email = ["support@salemove.com"]
|
15
|
+
gem.description = %q{It's lenjadoric}
|
16
|
+
gem.summary = %q{What description said}
|
17
|
+
gem.license = "MIT"
|
18
|
+
|
19
|
+
gem.files = `git ls-files`.split($/)
|
20
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
|
+
gem.require_paths = ["lib"]
|
23
|
+
|
24
|
+
gem.add_dependency 'lru_redux'
|
25
|
+
|
26
|
+
if RUBY_PLATFORM =~ /java/
|
27
|
+
gem.add_dependency 'jrjackson'
|
28
|
+
else
|
29
|
+
gem.add_dependency 'oj'
|
30
|
+
end
|
31
|
+
|
32
|
+
gem.add_development_dependency "bundler", "~> 1.3"
|
33
|
+
gem.add_development_dependency "rake"
|
34
|
+
gem.add_development_dependency "bunny"
|
35
|
+
gem.add_development_dependency "benchmark-ips"
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class Lenjador
|
4
|
+
module Adapters
|
5
|
+
class StdoutAdapter
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :logger
|
9
|
+
|
10
|
+
def_delegators :@logger, :debug?, :info?, :warn?, :error?, :fatal?
|
11
|
+
|
12
|
+
def initialize(level, *)
|
13
|
+
@logger = Logger.new(STDOUT)
|
14
|
+
@logger.level = level
|
15
|
+
end
|
16
|
+
|
17
|
+
def log(level, metadata = {})
|
18
|
+
message = metadata[:message]
|
19
|
+
data = metadata.select { |key, value| key != :message }
|
20
|
+
log_data = [
|
21
|
+
message,
|
22
|
+
data.empty? ? nil : Utils.generate_json(data)
|
23
|
+
].compact.join(' ')
|
24
|
+
|
25
|
+
@logger.public_send level, log_data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Adapters
|
3
|
+
class StdoutJsonAdapter
|
4
|
+
def initialize(level, service_name, *)
|
5
|
+
@level = level
|
6
|
+
@service_name = service_name
|
7
|
+
@application_name = Utils.application_name(service_name)
|
8
|
+
@mutex = Mutex.new if RUBY_ENGINE == "jruby"
|
9
|
+
end
|
10
|
+
|
11
|
+
def log(level, metadata = {})
|
12
|
+
if meets_threshold?(level)
|
13
|
+
message = Utils.build_event(metadata, level, @application_name)
|
14
|
+
print_line(Utils.generate_json(message))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def debug?
|
19
|
+
meets_threshold?(:debug)
|
20
|
+
end
|
21
|
+
|
22
|
+
def info?
|
23
|
+
meets_threshold?(:info)
|
24
|
+
end
|
25
|
+
|
26
|
+
def warn?
|
27
|
+
meets_threshold?(:warn)
|
28
|
+
end
|
29
|
+
|
30
|
+
def error?
|
31
|
+
meets_threshold?(:error)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fatal?
|
35
|
+
meets_threshold?(:fatal)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def meets_threshold?(level)
|
41
|
+
LOG_LEVELS.index(level.to_s) >= @level
|
42
|
+
end
|
43
|
+
|
44
|
+
# puts is atomic in MRI starting from 2.5.0
|
45
|
+
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.5.0"
|
46
|
+
def print_line(str)
|
47
|
+
$stdout.puts(str)
|
48
|
+
end
|
49
|
+
elsif RUBY_ENGINE == "jruby"
|
50
|
+
def print_line(str)
|
51
|
+
@mutex.synchronize { $stdout.write(str + "\n") }
|
52
|
+
end
|
53
|
+
else
|
54
|
+
def print_line(str)
|
55
|
+
$stdout.write(str + "\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Adapters
|
3
|
+
LOG_LEVELS = %w(debug info warn error fatal unknown).freeze
|
4
|
+
|
5
|
+
def self.get(type, service_name, arguments)
|
6
|
+
adapter =
|
7
|
+
if type == 'stdout'
|
8
|
+
if arguments.fetch(:json, false)
|
9
|
+
require_relative 'adapters/stdout_json_adapter'
|
10
|
+
StdoutJsonAdapter
|
11
|
+
else
|
12
|
+
require_relative 'adapters/stdout_adapter'
|
13
|
+
StdoutAdapter
|
14
|
+
end
|
15
|
+
else
|
16
|
+
raise "Unsupported logger: #{type}"
|
17
|
+
end
|
18
|
+
level = LOG_LEVELS.index(arguments.fetch(:level, 'debug'))
|
19
|
+
adapter.new(level, service_name, arguments)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Preprocessors
|
3
|
+
class Blacklist
|
4
|
+
|
5
|
+
DEFAULT_ACTION = 'prune'
|
6
|
+
MASK_SYMBOL = '*'
|
7
|
+
MASKED_VALUE = MASK_SYMBOL * 5
|
8
|
+
|
9
|
+
class UnsupportedActionException < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(config = {})
|
13
|
+
@fields_to_process = config[:fields].inject({}) do |mem, field|
|
14
|
+
key = field.delete(:key)
|
15
|
+
options = {action: DEFAULT_ACTION}.merge(field)
|
16
|
+
validate_action_supported(options[:action])
|
17
|
+
mem.merge(key => options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def process(data)
|
22
|
+
if data.is_a? Hash
|
23
|
+
data.inject({}) do |mem, (key, val)|
|
24
|
+
if (field = @fields_to_process[key.to_s])
|
25
|
+
self.send(action_method(field[:action]), mem, key, val)
|
26
|
+
else
|
27
|
+
mem.merge(key => process(val))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
elsif data.is_a? Array
|
31
|
+
data.inject([]) do |mem, val|
|
32
|
+
mem + [process(val)]
|
33
|
+
end
|
34
|
+
else
|
35
|
+
data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def action_method(action)
|
42
|
+
"#{action}_field"
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_action_supported(action)
|
46
|
+
unless self.respond_to?(action_method(action).to_sym, true)
|
47
|
+
raise UnsupportedActionException.new("Action: #{action} is not supported")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def mask_field(data, key, val)
|
52
|
+
data.merge(key => MASKED_VALUE)
|
53
|
+
end
|
54
|
+
|
55
|
+
def prune_field(data, *)
|
56
|
+
data
|
57
|
+
end
|
58
|
+
alias_method :exclude_field, :prune_field
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'lru_redux'
|
2
|
+
|
3
|
+
class Lenjador
|
4
|
+
module Preprocessors
|
5
|
+
class JSONPointerTrie
|
6
|
+
SEPARATOR = '/'.freeze
|
7
|
+
WILDCARD = '~'.freeze
|
8
|
+
DEFAULT_CACHE_SIZE = 100
|
9
|
+
|
10
|
+
def initialize(cache_size: DEFAULT_CACHE_SIZE, **)
|
11
|
+
@root_node = {}
|
12
|
+
@cache = LruRedux::Cache.new(cache_size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def insert(pointer)
|
16
|
+
split_path(pointer).reduce(@root_node) do |tree, key|
|
17
|
+
tree[key] ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def include?(path)
|
24
|
+
@cache.getset(path) { traverse_path(path) }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def traverse_path(path)
|
30
|
+
split_path(path).reduce(@root_node) do |node, key|
|
31
|
+
node[key] || node[WILDCARD] || (break false)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def split_path(path)
|
36
|
+
path.split(SEPARATOR).reject(&:empty?)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Preprocessors
|
3
|
+
module Strategies
|
4
|
+
class Mask
|
5
|
+
MASK_SYMBOL = '*'.freeze
|
6
|
+
MASKED_VALUE = MASK_SYMBOL * 5
|
7
|
+
|
8
|
+
def initialize(trie)
|
9
|
+
@trie = trie
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(data, pointer = '')
|
13
|
+
return MASKED_VALUE unless @trie.include?(pointer)
|
14
|
+
|
15
|
+
case data
|
16
|
+
when Hash
|
17
|
+
process_hash(data, pointer)
|
18
|
+
|
19
|
+
when Array
|
20
|
+
process_array(data, pointer)
|
21
|
+
|
22
|
+
else
|
23
|
+
data
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def process_hash(data, parent_pointer)
|
30
|
+
data.each_with_object({}) do |(key, value), result|
|
31
|
+
result[key] = process(value, "#{parent_pointer}/#{key}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_array(data, parent_pointer)
|
36
|
+
data.each_with_index.map do |value, index|
|
37
|
+
process(value, "#{parent_pointer}/#{index}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Preprocessors
|
3
|
+
module Strategies
|
4
|
+
class Prune
|
5
|
+
def initialize(trie)
|
6
|
+
@trie = trie
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(data, pointer = '')
|
10
|
+
return nil unless @trie.include?(pointer)
|
11
|
+
|
12
|
+
case data
|
13
|
+
when Hash
|
14
|
+
process_hash(data, pointer)
|
15
|
+
|
16
|
+
when Array
|
17
|
+
process_array(data, pointer)
|
18
|
+
|
19
|
+
else
|
20
|
+
data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def process_hash(data, parent_pointer)
|
27
|
+
data.each_with_object({}) do |(key, value), result|
|
28
|
+
path = "#{parent_pointer}/#{key}"
|
29
|
+
|
30
|
+
result[key] = process(value, path) if @trie.include?(path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_array(data, parent_pointer)
|
35
|
+
data.each_with_index.each_with_object([]) do |(value, index), result|
|
36
|
+
path = "#{parent_pointer}/#{index}"
|
37
|
+
|
38
|
+
result << process(value, path) if @trie.include?(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'lenjador/preprocessors/json_pointer_trie'
|
2
|
+
require 'lenjador/preprocessors/strategies/mask'
|
3
|
+
require 'lenjador/preprocessors/strategies/prune'
|
4
|
+
|
5
|
+
class Lenjador
|
6
|
+
module Preprocessors
|
7
|
+
class Whitelist
|
8
|
+
DEFAULT_WHITELIST = %w[/id /message /correlation_id /queue].freeze
|
9
|
+
MASK_SYMBOL = '*'.freeze
|
10
|
+
MASKED_VALUE = MASK_SYMBOL * 5
|
11
|
+
|
12
|
+
PRUNE_ACTION_NAMES = %w[prune exclude].freeze
|
13
|
+
|
14
|
+
class InvalidPointerFormatException < Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(config = {})
|
18
|
+
trie = build_trie(config)
|
19
|
+
|
20
|
+
@strategy = if PRUNE_ACTION_NAMES.include?(config[:action].to_s)
|
21
|
+
Strategies::Prune.new(trie)
|
22
|
+
else
|
23
|
+
Strategies::Mask.new(trie)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def process(data)
|
28
|
+
@strategy.process(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_pointer(pointer)
|
34
|
+
if pointer.slice(-1) == '/'
|
35
|
+
raise InvalidPointerFormatException, 'Pointer should not contain trailing slash'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def decode(pointer)
|
40
|
+
pointer
|
41
|
+
.gsub('~1', '/')
|
42
|
+
.gsub('~0', '~')
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_trie(config)
|
46
|
+
pointers = (config[:pointers] || []) + DEFAULT_WHITELIST
|
47
|
+
|
48
|
+
pointers.reduce(JSONPointerTrie.new(config)) do |trie, pointer|
|
49
|
+
validate_pointer(pointer)
|
50
|
+
|
51
|
+
trie.insert(decode(pointer))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Lenjador
|
2
|
+
module Preprocessors
|
3
|
+
def self.get(type, arguments)
|
4
|
+
preprocessor =
|
5
|
+
case type.to_s
|
6
|
+
when 'blacklist'
|
7
|
+
require_relative 'preprocessors/blacklist'
|
8
|
+
Preprocessors::Blacklist
|
9
|
+
when 'whitelist'
|
10
|
+
require_relative 'preprocessors/whitelist'
|
11
|
+
Preprocessors::Whitelist
|
12
|
+
else
|
13
|
+
raise "Unknown preprocessor: #{type}"
|
14
|
+
end
|
15
|
+
preprocessor.new(arguments)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|