diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 87a199240660f..45576361497fe 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,13 @@ +* Make `ActiveRecord::Encryption::Encryptor` agnostic of the serialization format used for encrypted data. + + Previously, the encryptor instance only allowed an encrypted value serialized as a `String` to be passed to the message serializer. + + Now, the encryptor lets the configured `message_serializer` decide which types of serialized encrypted values are supported. A custom serialiser is therefore allowed to serialize `ActiveRecord::Encryption::Message` objects using a type other than `String`. + + The default `ActiveRecord::Encryption::MessageSerializer` already ensures that only `String` objects are passed for deserialization. + + *Maxime Réty* + * Fix `encrypted_attribute?` to take into account context properties passed to `encrypts`. *Maxime Réty* diff --git a/activerecord/lib/active_record/encryption/encryptor.rb b/activerecord/lib/active_record/encryption/encryptor.rb index 3c13040cebf0b..1c19d9e5459ae 100644 --- a/activerecord/lib/active_record/encryption/encryptor.rb +++ b/activerecord/lib/active_record/encryption/encryptor.rb @@ -100,7 +100,6 @@ def serialize_message(message) end def deserialize_message(message) - raise Errors::Encoding unless message.is_a?(String) serializer.load message rescue ArgumentError, TypeError, Errors::ForbiddenClass raise Errors::Encoding diff --git a/activerecord/test/cases/encryption/encryptor_test.rb b/activerecord/test/cases/encryption/encryptor_test.rb index 8829a00c58bfc..1195a6cdfa830 100644 --- a/activerecord/test/cases/encryption/encryptor_test.rb +++ b/activerecord/test/cases/encryption/encryptor_test.rb @@ -12,7 +12,13 @@ class ActiveRecord::Encryption::EncryptorTest < ActiveRecord::EncryptionTestCase assert_encrypt_text("my secret text") end - test "decrypt and invalid string will raise a Decryption error" do + test "trying to decrypt something else than a string will raise a Decryption error" do + assert_raises(ActiveRecord::Encryption::Errors::Decryption) do + @encryptor.decrypt(:it_can_only_decrypt_strings) + end + end + + test "decrypt an invalid string will raise a Decryption error" do assert_raises(ActiveRecord::Encryption::Errors::Decryption) do @encryptor.decrypt("some test that does not make sense") end diff --git a/activerecord/test/cases/encryption/message_serializer_test.rb b/activerecord/test/cases/encryption/message_serializer_test.rb index 1e86b30690782..db81ad74a7f31 100644 --- a/activerecord/test/cases/encryption/message_serializer_test.rb +++ b/activerecord/test/cases/encryption/message_serializer_test.rb @@ -29,13 +29,13 @@ class ActiveRecord::Encryption::MessageSerializerTest < ActiveRecord::Encryption assert_nothing_raised { @serializer.load(class_loading_payload) } end - test "detects random JSON data and raises an invalid dencryption error" do + test "detects random JSON data and raises a decryption error" do assert_raises ActiveRecord::Encryption::Errors::Decryption do @serializer.load JSON.dump("hey there") end end - test "detects random JSON hashes and raises an invalid decryption error" do + test "detects random JSON hashes and raises a decryption error" do assert_raises ActiveRecord::Encryption::Errors::Decryption do @serializer.load JSON.dump({ some: "other data" }) end @@ -47,6 +47,12 @@ class ActiveRecord::Encryption::MessageSerializerTest < ActiveRecord::Encryption end end + test "raises a TypeError when trying to deserialize other data types" do + assert_raises TypeError do + @serializer.load(:it_can_only_deserialize_strings) + end + end + test "raises ForbiddenClass when trying to serialize other data types" do assert_raises ActiveRecord::Encryption::Errors::ForbiddenClass do @serializer.dump("it can only serialize messages!") diff --git a/guides/source/active_record_encryption.md b/guides/source/active_record_encryption.md index f0c43105c8f5d..5aa20d5586762 100644 --- a/guides/source/active_record_encryption.md +++ b/guides/source/active_record_encryption.md @@ -145,7 +145,7 @@ To encrypt Action Text fixtures, you should place them in `fixtures/action_text/ ### Supported Types -`active_record.encryption` will serialize values using the underlying type before encrypting them, but *they must be serializable as strings*. Structured types like `serialized` are supported out of the box. +`active_record.encryption` will serialize values using the underlying type before encrypting them, but, unless using a custom `message_serializer`, *they must be serializable as strings*. Structured types like `serialized` are supported out of the box. If you need to support a custom type, the recommended way is using a [serialized attribute](https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html). The declaration of the serialized attribute should go **before** the encryption declaration: