Skip to content

Commit

Permalink
Support encrypted attributes on columns with default values
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jorgemanrubia and fatkodima committed May 7, 2022
1 parent 480edd4 commit 238432d
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 3 deletions.
8 changes: 8 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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?`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
5 changes: 5 additions & 0 deletions activerecord/test/cases/encryption/encryptable_record_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<untitled>")
end

private
class FailingKeyProvider
def decryption_key(message) end
Expand Down
2 changes: 1 addition & 1 deletion activerecord/test/schema/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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: "<untitled>"
t.column :original_name, :string

t.datetime :created_at
Expand Down

0 comments on commit 238432d

Please sign in to comment.