Module: OpenLoam::Encryptable

Extended by:
ActiveSupport::Concern
Included in:
MfaCredential, PendingAction, SsoProvider
Defined in:
lib/open_loam/encryptable.rb

Overview

Field-level encryption at rest, keyed per tenant. Declare it on a model:

class Patient < OpenLoam::TenantRecord
include OpenLoam::Encryptable
encrypts :ssn                      # encrypted at rest, not searchable
encrypts :email, searchable: true  # + a blind index for exact-match lookup
end

Patient.create!(ssn: "078-05-1120")   # the ssn COLUMN now holds "v1:...."
patient.ssn                           # => "078-05-1120" (decrypted on read)
Patient.find_by_email("[email protected]")      # exact match via the blind index

The value is sealed with the CURRENT tenant's key (OpenLoam.tenant!), never the record's stored tenant_id — so a read in the wrong tenant's context fails the GCM auth tag instead of quietly decrypting another tenant's data. Reading or writing an encrypted field with no tenant in context raises MissingTenantError: you cannot encrypt without knowing whose key.

Encryption happens eagerly on assignment, so re-submitting a form with the same value re-seals it under a fresh IV and records a "[encrypted]" audit update with no real change — accepted prototype noise.

Instance Method Summary collapse

Instance Method Details

#open_loam_reencrypt! ⇒ Object

Re-seal every encrypted field under the current key, with fresh IVs — the per-record step of a key rotation (read old, write new). With HKDF-from- master, a real rotation means a new master or a bumped Cipher::VERSION; the version tag lets old and new ciphertext coexist while this runs across a tenant's records, so rotation is a lazy re-encrypt, not a stop-the-world migration. Writes an ordinary audited "[encrypted]" update — see lib/tasks/open_loam.rake (open_loam:encryption:rotate).



143
144
145
146
147
148
# File 'lib/open_loam/encryptable.rb', line 143

def open_loam_reencrypt!
  self.class.open_loam_encrypted_attributes.each do |name|
    public_send("#{name}=", public_send(name))
  end
  save!
end