danlherman/encrypt_column

View on GitHub
lib/encrypt_column/encrypt_column.rb

Summary

Maintainability
A
1 hr
Test Coverage
module ClassMethods
  # Encrypt any column with a hash (searchable: true) or conditionally(if: Proc)
  # also has a failsafe (failsafe: true) feature to write to a different db column
  # in the database, i.e. <name>_ciphertext. This prevents users from accidentally
  # commenting out the encrypt declaration and reading/writing plaintext to the
  # database.
  def encrypt(name, options = {})
    searchable = options[:searchable] || false
    encrypt_cond = options[:if] || proc { true }
    failsafe = options[:failsafe] || false
    @@encrypt_column_key = options[:key] || ENV['ENCRYPT_KEY']
    @@hash_salt = options[:hash_salt] || ENV['HASH_SALT']
    column = name
    column = "#{name}_ciphertext" if failsafe
    hash_column = "#{name}_hash"

    # getter
    define_method(name) do
      return read_attribute(column) unless instance_eval(&encrypt_cond)
      Decrypt.ciphertext(read_attribute(column), @@encrypt_column_key)
    end

    # setter
    define_method("#{name}=") do |value|
      return write_attribute(column, value) unless instance_eval(&encrypt_cond)
      write_attribute(column, Encrypt.plaintext(value, @@encrypt_column_key))
      write_attribute(hash_column, Hashed.val(value, @@hash_salt)) if searchable
    end

    # search method when searchable specified
    define_singleton_method("with_#{name}") do |value|
      where(hash_column.to_sym => Hashed.val(value, @@hash_salt))
    end if searchable
  end
end