Fix loading records with encrypted attributes defined on columns with default values
This commit is contained in:
parent
86a280a347
commit
8f12731b90
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user