Skip to content

Commit

Permalink
Handle serialized binary data in encrypted columns
Browse files Browse the repository at this point in the history
Calling `serialize` is not always possible, because the column type
might not expect to be serializing a String, for example when declared
as serialzed or store attribute.

With binary data the encryptor was passed an
`ActiveModel::Type::Binary::Data`` and returned a `String``. In order to
remain transparent we need to turn the data back into a
`ActiveModel::Type::Binary::Data` before passing it on.

We'll also rename `serialize`` to `text_to_database_type` to be a bit
more descriptive.
  • Loading branch information
djmb committed Feb 15, 2024
1 parent 53ca39f commit 38b7df7
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 5 deletions.
Expand Up @@ -100,7 +100,7 @@ def decrypt_as_text(value)
end

def decrypt(value)
reserialize decrypt_as_text(value)
text_to_database_type decrypt_as_text(value)
end

def try_to_deserialize_with_previous_encrypted_types(value)
Expand Down Expand Up @@ -140,7 +140,7 @@ def encrypt_as_text(value)
end

def encrypt(value)
reserialize encrypt_as_text(value)
text_to_database_type encrypt_as_text(value)
end

def encryptor
Expand All @@ -159,9 +159,9 @@ def clean_text_scheme
@clean_text_scheme ||= ActiveRecord::Encryption::Scheme.new(downcase: downcase?, encryptor: ActiveRecord::Encryption::NullEncryptor.new)
end

def reserialize(value)
if cast_type.binary?
cast_type.serialize(value)
def text_to_database_type(value)
if value && cast_type.binary?
ActiveModel::Type::Binary::Data.new(value)
else
value
end
Expand Down
7 changes: 7 additions & 0 deletions activerecord/test/cases/encryption/encryptable_record_test.rb
Expand Up @@ -397,6 +397,8 @@ def name
test "binary data can be encrypted" do
all_bytes = (0..255).map(&:chr).join
assert_equal all_bytes, EncryptedBookWithBinary.create!(logo: all_bytes).logo
assert_nil EncryptedBookWithBinary.create!(logo: nil).logo
assert_equal "", EncryptedBookWithBinary.create!(logo: "").logo
end

test "binary data can be encrypted uncompressed" do
Expand All @@ -406,6 +408,11 @@ def name
assert_equal high_bytes, EncryptedBookWithBinary.create!(logo: high_bytes).logo
end

test "serialized binary data can be encrypted" do
json_bytes = (32..127).map(&:chr)
assert_equal json_bytes, EncryptedBookWithSerializedBinary.create!(logo: json_bytes).logo
end

private
def build_derived_key_provider_with(hash_digest_class)
ActiveRecord::Encryption.with_encryption_context(key_generator: ActiveRecord::Encryption::KeyGenerator.new(hash_digest_class: hash_digest_class)) do
Expand Down
7 changes: 7 additions & 0 deletions activerecord/test/models/book_encrypted.rb
Expand Up @@ -49,3 +49,10 @@ class EncryptedBookWithBinary < ActiveRecord::Base

encrypts :logo
end

class EncryptedBookWithSerializedBinary < ActiveRecord::Base
self.table_name = "encrypted_books"

serialize :logo, coder: JSON
encrypts :logo
end

0 comments on commit 38b7df7

Please sign in to comment.