Fix loading records with encrypted attributes defined on columns with default values

This commit is contained in:
fatkodima 2022-09-07 23:30:18 +03:00
parent 86a280a347
commit 8f12731b90
3 changed files with 21 additions and 13 deletions

@ -82,8 +82,9 @@ def global_previous_schemes_for(scheme)
def encrypt_attribute(name, attribute_scheme)
encrypted_attributes << name.to_sym
attribute name, default: -> { columns_hash[name.to_s]&.default } do |cast_type|
ActiveRecord::Encryption::EncryptedAttributeType.new scheme: attribute_scheme, cast_type: cast_type
attribute name do |cast_type|
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: attribute_scheme, cast_type: cast_type,
default: -> { columns_hash[name.to_s]&.default })
end
preserve_original_encrypted(name) if attribute_scheme.ignore_case?

@ -20,11 +20,12 @@ class EncryptedAttributeType < ::ActiveRecord::Type::Text
# * <tt>:scheme</tt> - A +Scheme+ with the encryption properties for this attribute.
# * <tt>:cast_type</tt> - A type that will be used to serialize (before encrypting) and deserialize
# (after decrypting). ActiveModel::Type::String by default.
def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type: false)
def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type: false, default: nil)
super()
@scheme = scheme
@cast_type = cast_type
@previous_type = previous_type
@default = default
end
def deserialize(value)
@ -40,7 +41,7 @@ def serialize(value)
end
def changed_in_place?(raw_old_value, new_value)
old_value = raw_old_value.nil? ? nil : deserialize_previous_value_to_determine_change(raw_old_value)
old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
old_value != new_value
end
@ -70,7 +71,15 @@ def previous_type?
def decrypt(value)
with_context do
encryptor.decrypt(value, **decryption_options) unless value.nil?
unless value.nil?
default = @default.call if @default
if default && default == value
value
else
encryptor.decrypt(value, **decryption_options)
end
end
end
rescue ActiveRecord::Encryption::Errors::Base => error
if previous_types_without_clean_text.blank?
@ -135,14 +144,6 @@ def decryption_options
def clean_text_scheme
@clean_text_scheme ||= ActiveRecord::Encryption::Scheme.new(downcase: downcase?, encryptor: ActiveRecord::Encryption::NullEncryptor.new)
end
def deserialize_previous_value_to_determine_change(raw_old_value)
deserialize(raw_old_value)
# We tolerate unencrypted data when determining if a column changed
# to support default DB values in encrypted attributes
rescue ActiveRecord::Encryption::Errors::Decryption
nil
end
end
end
end

@ -297,6 +297,12 @@ def name
assert_encrypted_attribute(book, :name, "<untitled>")
end
test "loading records with encrypted attributes defined on columns with default values" do
EncryptedBook.insert({ format: "ebook" })
book = EncryptedBook.last
assert_equal "<untitled>", book.name
end
private
class FailingKeyProvider
def decryption_key(message) end