module EncryptedStore::ActiveRecord::Mixin

Public Class Methods

descendants() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 14
def descendants
  Rails.application.eager_load! if defined?(Rails) && Rails.application
  ::ActiveRecord::Base.descendants.select { |model| model < Mixin }
end
descendants?() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 19
def descendants?
  !descendants.empty?
end
included(base) click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 7
def included(base)
  base.before_save(:_encrypted_store_save,
    if: :encrypted_attributes_changed?)

  base.extend(ClassMethods)
end

Public Instance Methods

_crypto_hash() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 72
def _crypto_hash
  @_crypto_hash || _decrypt_encrypted_store
end
_decrypt_encrypted_store() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 76
def _decrypt_encrypted_store
  @_crypto_hash = CryptoHash.decrypt(_decrypted_key, self.encrypted_store)
end
_decrypted_key() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 92
def _decrypted_key
  EncryptedStore.retrieve_dek(EncryptionKey, _encryption_key_id)
end
_encrypt_encrypted_store() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 80
def _encrypt_encrypted_store
  iter_mag = EncryptedStore.config.iteration_magnitude? ?
             EncryptedStore.config.iteration_magnitude  :
             -1

  self.encrypted_store = _crypto_hash.encrypt(
    _decrypted_key,
    EncryptionKeySalt.generate_salt(_encryption_key_id),
    iter_mag
  )
end
_encrypted_store_data() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 64
def _encrypted_store_data
  self.class._encrypted_store_data
end
_encrypted_store_get(field) click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 96
def _encrypted_store_get(field)
  _crypto_hash[field]
end
_encrypted_store_save() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 113
def _encrypted_store_save
  _crypto_hash # make sure we have a cached copy of the decrypted data.
  _encrypted_store_sync_key
  _encrypt_encrypted_store
end
_encrypted_store_set(field, value) click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 100
def _encrypted_store_set(field, value)
  attribute_will_change!(field)
  _crypto_hash[field] = value
end
_encrypted_store_sync_key() click to toggle source

Locks the record (although doesn't reload the attributes) and updates the encryption_key_id if it has changed since the record was originally loaded.

# File lib/encrypted_store/active_record/mixin.rb, line 123
def _encrypted_store_sync_key
  unless new_record?
    # Obtain a lock without overriding attribute values for this
    # instance. Here `record` will be an updated version of this instance.
    record = self.class.unscoped { self.class.lock.find(id) }

    if record && record.encryption_key_id
      self.encryption_key_id = record.encryption_key_id
    end
  end
end
_encryption_key_id() click to toggle source
# File lib/encrypted_store/active_record/mixin.rb, line 68
def _encryption_key_id
  self.encryption_key_id ||= EncryptionKey.primary_encryption_key.id
end
encrypted_attributes_changed?() click to toggle source

Checks if any of the encrypted attributes are in the list of changed attributes

# File lib/encrypted_store/active_record/mixin.rb, line 108
def encrypted_attributes_changed?
  !(changed.map(&:to_sym) & _encrypted_store_data[:encrypted_attributes])
    .empty?
end
purge_encrypted_data() click to toggle source

Completely purges encrypted data for a record

# File lib/encrypted_store/active_record/mixin.rb, line 57
def purge_encrypted_data
  self.encrypted_store = nil
  self.encryption_key_id = nil
  @_crypto_hash = nil
  save!(validate: false)
end
reencrypt(encryption_key) click to toggle source

Instance Methods

# File lib/encrypted_store/active_record/mixin.rb, line 44
def reencrypt(encryption_key)
  with_lock do
    # Must decrypt any changes made to the encrypted data, before updating
    # the encryption_key_id.
    _decrypt_encrypted_store
    self.encryption_key_id = encryption_key.id
    _encrypt_encrypted_store
    save!(validate: false)
  end
end