class Fluent::Plugin::AnonymizerFilter

Constants

MASK_METHODS

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/fluent/plugin/filter_anonymizer.rb, line 99
def initialize
  super
  @salt_list = []
  @salt_map = {}
  @conversions = []
end

Public Instance Methods

configure(conf) click to toggle source
Calls superclass method
# File lib/fluent/plugin/filter_anonymizer.rb, line 106
def configure(conf)
  super

  salt_missing = false

  @salt_list << @salt
  @salt_list += @salts if @salts

  @masks = []
  @mask_config_list.each do |c|
    unless c.salt || @salt_list.size > 0
      salt_missing = true
    end

    conv = MASK_METHODS[c.method].call(c)
    [c.key || nil, *c.keys].compact.each do |key|
      @masks << masker_for_key(conv, key, c)
    end
    [c.key_chain || nil, *c.key_chains].compact.each do |key_chain|
      @masks << masker_for_key_chain(conv, key_chain.split('.'), c)
    end
    @masks << masker_for_key_pattern(conv, c.key_pattern, c) if c.key_pattern
    @masks << masker_for_value_pattern(conv, c.value_pattern, c) if c.value_pattern
    @masks << masker_for_value_in_subnet(conv, c.value_in_subnet, c) if c.value_in_subnet
  end

  # obsolete option handling
  [[@md5_keys,:md5],[@sha1_keys,:sha1],[@sha256_keys,:sha256],[@sha384_keys,:sha384],[@sha512_keys,:sha512]].each do |param,m|
    next unless param
    @salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
    conf = OpenStruct.new
    conf.salt = @hash_salt || ''
    conf.mask_array_elements = true
    conv = MASK_METHODS[m].call(conf)
    param.split(',').map(&:strip).each do |key|
      if key.include?('.')
        @masks << masker_for_key_chain(conv, key.split('.'), conf)
      else
        @masks << masker_for_key(conv, key, conf)
      end
    end
  end
  if @ipaddr_mask_keys
    @salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
    conf = OpenStruct.new
    conf.salt = @hash_salt || ''
    conf.mask_array_elements = true
    conf.ipv4_mask_bits = @ipv4_mask_subnet
    conf.ipv6_mask_bits = @ipv6_mask_subnet
    conv = MASK_METHODS[:network].call(conf)
    @ipaddr_mask_keys.split(',').map(&:strip).each do |key|
      if key.include?('.')
        @masks << masker_for_key_chain(conv, key.split('.'), conf)
      else
        @masks << masker_for_key(conv, key, conf)
      end
    end
  end

  if @masks.size < 1
    raise Fluent::ConfigError, "no anonymizing operations configured"
  end
  if salt_missing
    raise Fluent::ConfigError, "salt (or salts) required, but missing"
  end
end
filter(tag, time, record) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 173
def filter(tag, time, record)
  record.update(@masks.reduce(record){|r,mask| mask.call(r)})
end
mask_value(value, for_each) { |v| ... } click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 188
def mask_value(value, for_each)
  if for_each && value.is_a?(Array)
    value.map{|v|
      yield v
    }
  else
    yield value
  end
end
masker_for_key(conv, key, opts) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 198
def masker_for_key(conv, key, opts)
  for_each = opts.mask_array_elements
  salt = opts.salt || salt_determine(key)
  if for_each
    ->(record){
      begin
        if record.has_key?(key)
          record[key] = mask_value(record[key], for_each){|v| conv.call(v, salt) }
        end
      rescue => e
        log.error "unexpected error while masking value", error_class: e.class, error: e.message
      end
      record
    }
  else
    ->(record){
      begin
        if record.has_key?(key)
          record[key] = conv.call(record[key], salt)
        end
      rescue => e
        log.error "unexpected error while masking value", error_class: e.class, error: e.message
      end
      record
    }
  end
end
masker_for_key_chain(conv, key_chain, opts) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 226
def masker_for_key_chain(conv, key_chain, opts)
  for_each = opts.mask_array_elements
  heading = key_chain[0..-2]
  container_fetcher = ->(record){ heading.reduce(record){|r,c| r && r.has_key?(c) ? r[c] : nil } }
  tailing = key_chain[-1]
  ->(record){
    begin
      container = container_fetcher.call(record)
      if container && container.has_key?(tailing)
        container[tailing] = mask_value(container[tailing], for_each){|v| conv.call(v, opts.salt || salt_determine(tailing)) }
      end
    rescue => e
      log.error "unexpected error while masking value", error_class: e.class, error: e.message
    end
    record
  }
end
masker_for_key_pattern(conv, pattern, opts) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 244
def masker_for_key_pattern(conv, pattern, opts)
  for_each = opts.mask_array_elements
  regexp = Regexp.new(pattern)
  ->(record){
    begin
      record.each_pair do |key, value|
        next unless (regexp =~ key.to_s rescue nil)
        record[key] = mask_value(record[key], for_each){|v| conv.call(v, opts.salt || salt_determine(key)) }
      end
    rescue => e
      log.error "unexpected error while masking value", error_class: e.class, error: e.message
    end
    record
  }
end
masker_for_value_in_subnet(conv, network_str, opts) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 275
def masker_for_value_in_subnet(conv, network_str, opts)
  network = IPAddr.new(network_str)
  ->(record){
    begin
      record.each_pair do |key, value|
        next unless (network.include?(value) rescue nil)
        record[key] = conv.call(value, opts.salt || salt_determine(key))
      end
    rescue => e
      log.error "unexpected error while masking value", error_class: e.class, error: e.message
    end
    record
  }
end
masker_for_value_pattern(conv, pattern, opts) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 260
def masker_for_value_pattern(conv, pattern, opts)
  regexp = Regexp.new(pattern)
  ->(record){
    begin
      record.each_pair do |key, value|
        next unless (regexp =~ value.to_s rescue nil)
        record[key] = conv.call(value, opts.salt || salt_determine(key))
      end
    rescue => e
      log.error "unexpected error while masking value", error_class: e.class, error: e.message
    end
    record
  }
end
salt_determine(key) click to toggle source
# File lib/fluent/plugin/filter_anonymizer.rb, line 177
def salt_determine(key)
  return @salt_map[key] if @salt_map.has_key?(key)
  keystr = key.to_s
  if keystr.empty?
    @salt_map[key] = @salt_list[0]
  else
    @salt_map[key] = @salt_list[(keystr[0].ord + keystr[-1].ord) % @salt_list.size]
  end
  @salt_map[key]
end