Top Level Namespace

Defined Under Namespace

Modules: ActiveRecord, SymmetricEncryption Classes: SymmetricEncryptionValidator

Instance Method Summary collapse

Instance Method Details

#encryptedField

Add :encrypted option for Mongoid models

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
  field :encrypted_life_history,           :type => String, :encrypted => {:compress => true, :random_iv => true}
end

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

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

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.encrypted_social_security_number = SymmetricEncryption.encrypt("123456789")

Note: Only “String” types are currently supported for encryption

Note: Unlike attr_encrypted finders must use the encrypted field name

Invalid Example, does not work:
  person = Person.where(:social_security_number => '123456789').first

Valid Example:
  person = Person.where(:encrypted_social_security_number => SymmetricEncryption.encrypt('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 :social_security_number, :type => String, :encrypted => {:compress => false, :random_iv => false}
field :sensitive_text, :type => String, :encrypted => {:compress => true, :random_iv => true}

Parameters:

  • name (Symbol)

    The name of the field.

  • options (Hash)

    The options to pass to the field.

Returns:

  • (Field)

    The generated field



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
# File 'lib/symmetric_encryption/mongoid.rb', line 79

Mongoid::Fields.option :encrypted do |model, field, options|
  if options != false
    options = options.is_a?(Hash) ? options.dup : {}
    field_name = field.name

    decrypt_as = options.delete(:decrypt_as)
    if decrypt_as.nil? && field_name.to_s.start_with?('encrypted_')
      decrypt_as = field_name.to_s['encrypted_'.length..-1]
    else
      raise "SymmetricEncryption for Mongoid. Encryption enabled for field #{field_name}. It must either start with 'encrypted_' or the option :decrypt_as must be supplied"
    end

    random_iv = options.delete(:random_iv) || false
    compress  = options.delete(:compress) || false

    # Generate getter and setter methods
    model.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      # Set the un-encrypted field
      # Also updates the encrypted field with the encrypted value
      def #{decrypt_as}=(value)
        @stored_#{field_name} = ::SymmetricEncryption.encrypt(value,#{random_iv},#{compress})
        self.#{field_name} = @stored_#{field_name}
        @#{decrypt_as} = value.freeze
      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}).freeze
          @stored_#{field_name} = self.#{field_name}
        end
        @#{decrypt_as}
      end
    EOS
  end
end