Support encrypted attributes on columns with default values

Before, it was failing to read these columns because their contents
were not encrypted, unless you enabled `config.active_record.encryption.support_unencrypted_data`.

Now, it will detect the change at creation time for these records and
effectively encrypt those values, preventing the problem.

Fixes #44314 #43664
Closes #44993

Co-authored-by: Dima Fatko <fatkodima123@gmail.com>
This commit is contained in:
Jorge Manrubia 2022-05-06 14:31:10 +02:00
parent 480edd4906
commit 238432d1cb
No known key found for this signature in database
GPG Key ID: 0709C38AA5CD2251
5 changed files with 24 additions and 3 deletions

View File

@ -1,3 +1,11 @@
* Support encrypted attributes on columns with default db values.
This adds support for encrypted attributes defined on columns with default values.
It will encrypt those values at creation time. Before, it would raise an
error unless `config.active_record.encryption.support_unencrypted_data` was true.
*Jorge Manrubia* and *Dima Fatko*
* Allow overriding `reading_request?` in `DatabaseSelector::Resolver`
The default implementation checks if a request is a `get?` or `head?`,

View File

@ -82,7 +82,7 @@ module ActiveRecord
def encrypt_attribute(name, attribute_scheme)
encrypted_attributes << name.to_sym
attribute name do |cast_type|
attribute name, default: -> { columns_hash[name.to_s]&.default } do |cast_type|
ActiveRecord::Encryption::EncryptedAttributeType.new scheme: attribute_scheme, cast_type: cast_type
end

View File

@ -40,7 +40,7 @@ module ActiveRecord
end
def changed_in_place?(raw_old_value, new_value)
old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
old_value = raw_old_value.nil? ? nil : deserialize_previous_value_to_determine_change(raw_old_value)
old_value != new_value
end
@ -135,6 +135,14 @@ module ActiveRecord
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

View File

@ -292,6 +292,11 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption
assert_equal Encoding::US_ASCII, book.reload.name.encoding
end
test "support encrypted attributes defined on columns with default values" do
book = EncryptedBook.create!
assert_encrypted_attribute(book, :name, "<untitled>")
end
private
class FailingKeyProvider
def decryption_key(message) end

View File

@ -141,7 +141,7 @@ ActiveRecord::Schema.define do
create_table :encrypted_books, id: :integer, force: true do |t|
t.references :author
t.string :format
t.column :name, :string
t.column :name, :string, default: "<untitled>"
t.column :original_name, :string
t.datetime :created_at