From 238432d1cb5b47d61764fb54fc3d701c1def852d Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Fri, 6 May 2022 14:31:10 +0200 Subject: [PATCH] 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 --- activerecord/CHANGELOG.md | 8 ++++++++ .../lib/active_record/encryption/encryptable_record.rb | 2 +- .../encryption/encrypted_attribute_type.rb | 10 +++++++++- .../test/cases/encryption/encryptable_record_test.rb | 5 +++++ activerecord/test/schema/schema.rb | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f34890565ed0c..f9f977ac8ba1d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -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?`, diff --git a/activerecord/lib/active_record/encryption/encryptable_record.rb b/activerecord/lib/active_record/encryption/encryptable_record.rb index cf7cede8ee0b7..04e84c44c885b 100644 --- a/activerecord/lib/active_record/encryption/encryptable_record.rb +++ b/activerecord/lib/active_record/encryption/encryptable_record.rb @@ -82,7 +82,7 @@ def global_previous_schemes_for(scheme) 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 diff --git a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb index e0979ca987bc8..0dfb8e356aba2 100644 --- a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb +++ b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb @@ -40,7 +40,7 @@ def serialize(value) 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 @@ 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 diff --git a/activerecord/test/cases/encryption/encryptable_record_test.rb b/activerecord/test/cases/encryption/encryptable_record_test.rb index 40f2e76a98eea..3fc8aa7c666a9 100644 --- a/activerecord/test/cases/encryption/encryptable_record_test.rb +++ b/activerecord/test/cases/encryption/encryptable_record_test.rb @@ -292,6 +292,11 @@ def name 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, "") + end + private class FailingKeyProvider def decryption_key(message) end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 89c3f85304f03..cb81bf14fdf5c 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -141,7 +141,7 @@ 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: "" t.column :original_name, :string t.datetime :created_at