I made a custom ActiveModel::Type
to encrypt a field before it goes to the database. The context is that multiple apps use the same database. Some write to it, some read from it. Now my side is write-only. I should never read the field, neither can I decrypt it.
How do we handle write-only fields with ActiveRecord?
- Should I raise?
- Should I return
nil
? - Should I return the ciphertext stored in the database?
- Is an
ActiveModel::Type
not made for this? e.g. Devise doesn’t use it. - What do other people do?
This is my sketch. It raises:
class RsaEncryptedType < ActiveModel::Type::String
def initialize(public_key: nil, private_key: nil)
raise "At least public_key or private_key must be present" unless public_key.present? || private_key.present?
@private_key = OpenSSL::PKey::RSA.new(private_key) if private_key.present?
@public_key = OpenSSL::PKey::RSA.new(public_key) if public_key.present?
end
def type
:rsa_encrypted
end
def serialize(value)
return nil if value.blank?
raise "Cannot encrypt. Public key missing." if @public_key.nil?
ciphertext = @public_key.public_encrypt(value.to_s)
Base64.strict_encode64(ciphertext)
end
def deserialize(value)
return nil if value.blank?
raise "Cannot decrypt. Private key missing." if @private_key.nil?
ciphertext = Base64.decode64(value)
@private_key.private_decrypt(ciphertext)
end
end