Module: Mongoid::Fields::ClassMethods

Defined in:
lib/symmetric_encryption/extensions/mongoid/fields.rb

Instance Method Summary collapse

Instance Method Details

#field_with_symmetric_encryption(field_name, options = {}) ⇒ Field

Example:

require 'mongoid'
require 'symmetric-encryption'

# Initialize Mongoid in a standalone environment. In a Rails app this is not required
Mongoid.logger = Logger.new($stdout)
Mongoid.load!('config/mongoid.yml')

# Initialize SymmetricEncryption in a standalone environment. In a Rails app this is not required
SymmetricEncryption.load!('config/symmetric-encryption.yml', 'test')

class Person
  include Mongoid::Document

  field :name,                             :type => String
  field :encrypted_social_security_number, :type => String, :encrypted => true
  field :age,                              :type => Integer

end

The above document results in the following document in the Mongo collection ‘persons’:

"name" : "Joe",
"encrypted_social_security_number" : "...",
"age"  : 21

Symmetric Encryption creates the getters and setters to be able to work with the field in it’s unencrypted form. For example

Example:

person = Person.where(:encrypted_social_security_number => '...').first

puts "Decrypted Social Security Number is: #{person.social_security_number}"

# Or is the same as
puts "Decrypted Social Security Number is: #{SymmetricEncryption.decrypt(person.encrypted_social_security_number)}"

# Sets the encrypted_social_security_number to encrypted version
person.social_security_number = "123456789"

# Or, is equivalent to:
person.social_security_number = SymmetricEncryption.encrypt("123456789")

Note: Unlike attr_encrypted finders must use the encrypted field name

For Example this is NOT valid:
  person = Person.where(:social_security_number => '123456789').first

Defines all the fields that are accessible on the Document For each field that is defined, a getter and setter will be added as an instance method to the Document.

Examples:

Define a field.

field :score, :type => Integer, :default => 0

Parameters:

  • name (Symbol)

    The name of the field.

  • options (Hash) (defaults to: {})

    The options to pass to the field.

Options Hash (options):

  • :encryption (Boolean)

    If the field contains encrypted data.

  • :decrypt_as (Symbol)

    Name of the getters and setters to generate to access the decrypted value of this field.

  • :type (Class)

    The type of the field.

  • :label (String)

    The label for the field.

  • :default (Object, Proc)

    The field’s default

Returns:

  • (Field)

    The generated field



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/symmetric_encryption/extensions/mongoid/fields.rb', line 77

def field_with_symmetric_encryption(field_name, options={})
  if options.delete(:encrypted) == true
    decrypt_as = options.delete(:decrypt_as)
    unless decrypt_as
      raise "SymmetricEncryption for Mongoid. When encryption is enabled for a field it must either start with 'encrypted_' or the option :decrypt must be supplied" unless field_name.to_s.start_with?('encrypted_')
      decrypt_as = field_name.to_s['encrypted_'.length..-1]
    end

    # Store Intended data type for this field, but we store it as a String
    underlying_type = options[:type]
    options[:type] = String

    raise "SymmetricEncryption for Mongoid currently only supports :type => String" unless underlying_type == String

    # #TODO Need to do type conversions. Currently only support String

    # Generate getter and setter methods
    class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      # Set the un-encrypted bank account number
      # Also updates the encrypted field with the encrypted value
      def #{decrypt_as}=(value)
        @stored_#{field_name} = SymmetricEncryption.encrypt(value)
        self.#{field_name} = @stored_#{field_name}
        @#{decrypt_as} = value
      end

      # Returns the decrypted value for the encrypted field
      # The decrypted value is cached and is only decrypted if the encrypted value has changed
      # If this method is not called, then the encrypted value is never decrypted
      def #{decrypt_as}
        if @stored_#{field_name} != self.#{field_name}
          @#{decrypt_as} = SymmetricEncryption.decrypt(self.#{field_name})
          @stored_#{field_name} = self.#{field_name}
        end
        @#{decrypt_as}
      end
    EOS
  end

  # Pass on to the regular Mongoid field method
  field_without_symmetric_encryption(field_name, options)
end