class HashRedactor::HashRedactor

Public Class Methods

new(opts = {}) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 5
def initialize(opts = {})
      @options = default_options.merge opts

      @options[:encode] = @options[:default_encoding] if @options[:encode] == true
      @options[:encode_iv] = @options[:default_encoding] if @options[:encode_iv] == true
      
      raise "unknown filter mode #{@options[:filter_mode]}" unless [:blacklist,:whitelist].include? @options[:filter_mode]
end

Public Instance Methods

decrypt(data, opts = {}) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 121
def decrypt(data, opts = {})
  options = @options.merge opts

      redact_hash = options[:redact]

      raise "Don't know what to decrypt. Please configure the redact hash when initializing or pass as an argument to #decrypt." unless redact_hash && redact_hash.any?

      raise "No encryption key specified. Please pass :encryption_key in options to new or decrypt" unless options[:encryption_key]
  
      result = data.clone
  
      redact_hash.each do |hash_key,how|
        if (how.to_sym == :encrypt)
          decrypt_value(result, hash_key, options)
        end
      end
  
      result
end
decrypt_value(result, hash_key, options) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 141
def decrypt_value(result, hash_key, options)
      data_key = 'encrypted_' + hash_key.to_s
      iv_key = 'encrypted_' + hash_key.to_s + '_iv'
      
      if hash_key.is_a? Symbol
        data_key = data_key.to_sym
        iv_key = iv_key.to_sym
      end

      if (result.has_key? data_key)
        if result[data_key].nil?
            decrypted_value = nil
        else
                iv = result[iv_key]
                crypt_key = options[:encryption_key]

                encrypted_value = result[data_key]

                # Decode if necessary
                iv = iv.unpack(options[:encode_iv]).first if options[:encode_iv]
                encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]

                decrypted_value = EncryptorInterface.decrypt(:data, encrypted_value,
                         iv: iv, key: crypt_key)
                         
                result.delete data_key
                result.delete iv_key
        end
        
        result[hash_key] = decrypted_value
      end
end
default_options() click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 14
def default_options
   {
     digest_salt:             "",
        encryption_key:      nil,
        encode:            true,
        encode_iv:         true,
        default_encoding:  'm',
        filter_mode:                 :blacklist,
        digest_empty:                true
   }
end
digest(hash, hash_key, options) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 79
def digest(hash, hash_key, options)
  dig_key = digest_key hash_key

     # Don't digest the value if the user wants empty values to be untouched
     if ((hash[hash_key].to_s != '') || options[:digest_empty]) 
              hash[dig_key] = Digest::SHA256.base64digest(
                                                                      hash[hash_key].to_s + options[:digest_salt])
      else
              hash[dig_key] = hash[hash_key]
      end
end
digest_key(hash_key) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 174
def digest_key hash_key
     dig_key = hash_key.to_s + '_digest'
     dig_key = dig_key.to_sym if hash_key.is_a? Symbol
     dig_key
end
encrypt(hash, hash_key, options) click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 91
def encrypt(hash, hash_key, options)
      raise "No encryption key specified. Please pass :encryption_key in options to new or redact" unless options[:encryption_key]

      data_key = 'encrypted_' + hash_key.to_s
      iv_key = 'encrypted_' + hash_key.to_s + '_iv'
      
      if hash_key.is_a? Symbol
              data_key = data_key.to_sym
              iv_key = iv_key.to_sym
      end

      # Don't try to encrypt nil
      if hash[hash_key].nil?
          encrypted_value = nil
          iv = nil
      else
              crypt_key = options[:encryption_key]
              iv = SecureRandom.random_bytes(12)
      
              encrypted_value = EncryptorInterface.encrypt(:data,
                                                       hash[hash_key], iv: iv, key: crypt_key)
      
              encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
              iv = [iv].pack(options[:encode_iv]) if options[:encode_iv]
      end
        
      hash[data_key] = encrypted_value
      hash[iv_key] = iv
end
options() click to toggle source
# File lib/hash_redactor/hash_redactor.rb, line 26
def options
   @options
end
redact(data, opts = {}) click to toggle source

Removes, digests or encrypts fields in a hash NOTE: This should NOT be used to protect password fields or similar The purpose of hashing is to reduce data to a form that can be quickly compared against other records without revealing the original value. To allow for this, all hashes are created using the *same salt* which is not secure enough for password protection For passwords, use BCrypt

# File lib/hash_redactor/hash_redactor.rb, line 37
def redact(data, opts = {})
  options = @options.merge(opts)
  
      redact_hash = options[:redact]
      
      raise "Don't know what to redact. Please configure the redact hash when initializing or pass as an argument to redact." unless redact_hash && redact_hash.any?
  
      result = data.clone
  
      # If it's blacklist mode, just go over our redact keys
      # Otherwise, we have to go through and remove all keys that aren't :keep
      if (options[:filter_mode] == :whitelist)
        keys = data.keys
        redact_hash = whitelist_redact_hash redact_hash
      else
        keys = redact_hash.keys
      end

      keys.each do |hash_key,how|
        how = redact_hash[hash_key] || :remove
        
        if data.has_key? hash_key
              case how.to_sym
                when :keep
                  nil
                when :remove
                      nil
                when :digest
                   digest(result, hash_key, options)
                when :encrypt
                      encrypt(result, hash_key, options)
                else
                      raise "redact called with unknown operation on #{hash_key}: #{how}"
              end

              result.delete hash_key unless how.to_sym == :keep
        end
      end
  
      result
end
whitelist_redact_hash(redact_hash) click to toggle source

Calculate all keys that should be kept in whitelist mode In multiple iterations of redact -> decrypt - digest keys will remain and then get deleted in the second iteration, so we have to add the digest keys so they're not wiped out on later iteration

# File lib/hash_redactor/hash_redactor.rb, line 184
def whitelist_redact_hash redact_hash
  digest_hash = {}
  
   redact_hash.each do |key,how|
     if (how.to_sym == :digest)
       digest_hash[digest_key(key)] = :keep
     end
   end
   
   digest_hash.merge redact_hash
end