Skip to content

Commit d0f3b00

Browse files
maximeretyeileencodes
authored andcommitted
[Fix rails#48685] Make the encryptor agnostic of the type of data to decrypt
It is the role of the underlying serializer to accept or reject the data to decrypt depending on its type. This behavior mirrors what is done at encryption, where the serializer asserts that the input is an ActiveRecord::Encryption::Message. This change allows for a wider variety of custom serializers, but does not change the behavior when using the default MessageSerializer class. Indeed, the default message serializer will raise a TypeError when invoking JSON.parse on any non-String input. This error will subsequently be translated into an ActiveRecord::Encryption::Errors::Encoding error by the encryptor, which does not change the current behavior at the encryptor level. A new test asserts that the default MessageSerializer is able to reject unexpected data types on its own at decryption time, just as it does at encryption time (test already present). The test also asserts that an exception is translated into an ActiveRecord::Encryption::Error::Encoding error at the encryptor level.
1 parent 1866ea9 commit d0f3b00

File tree

5 files changed

+26
-5
lines changed

5 files changed

+26
-5
lines changed

activerecord/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
* Make `ActiveRecord::Encryption::Encryptor` agnostic of the serialization format used for encrypted data.
2+
3+
Previously, the encryptor instance only allowed an encrypted value serialized as a `String` to be passed to the message serializer.
4+
5+
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`.
6+
7+
The default `ActiveRecord::Encryption::MessageSerializer` already ensures that only `String` objects are passed for deserialization.
8+
9+
*Maxime Réty*
10+
111
* Fix `encrypted_attribute?` to take into account context properties passed to `encrypts`.
212

313
*Maxime Réty*

activerecord/lib/active_record/encryption/encryptor.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ def serialize_message(message)
100100
end
101101

102102
def deserialize_message(message)
103-
raise Errors::Encoding unless message.is_a?(String)
104103
serializer.load message
105104
rescue ArgumentError, TypeError, Errors::ForbiddenClass
106105
raise Errors::Encoding

activerecord/test/cases/encryption/encryptor_test.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ class ActiveRecord::Encryption::EncryptorTest < ActiveRecord::EncryptionTestCase
1212
assert_encrypt_text("my secret text")
1313
end
1414

15-
test "decrypt and invalid string will raise a Decryption error" do
15+
test "trying to decrypt something else than a string will raise a Decryption error" do
16+
assert_raises(ActiveRecord::Encryption::Errors::Decryption) do
17+
@encryptor.decrypt(:it_can_only_decrypt_strings)
18+
end
19+
end
20+
21+
test "decrypt an invalid string will raise a Decryption error" do
1622
assert_raises(ActiveRecord::Encryption::Errors::Decryption) do
1723
@encryptor.decrypt("some test that does not make sense")
1824
end

activerecord/test/cases/encryption/message_serializer_test.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ class ActiveRecord::Encryption::MessageSerializerTest < ActiveRecord::Encryption
2929
assert_nothing_raised { @serializer.load(class_loading_payload) }
3030
end
3131

32-
test "detects random JSON data and raises an invalid dencryption error" do
32+
test "detects random JSON data and raises a decryption error" do
3333
assert_raises ActiveRecord::Encryption::Errors::Decryption do
3434
@serializer.load JSON.dump("hey there")
3535
end
3636
end
3737

38-
test "detects random JSON hashes and raises an invalid decryption error" do
38+
test "detects random JSON hashes and raises a decryption error" do
3939
assert_raises ActiveRecord::Encryption::Errors::Decryption do
4040
@serializer.load JSON.dump({ some: "other data" })
4141
end
@@ -47,6 +47,12 @@ class ActiveRecord::Encryption::MessageSerializerTest < ActiveRecord::Encryption
4747
end
4848
end
4949

50+
test "raises a TypeError when trying to deserialize other data types" do
51+
assert_raises TypeError do
52+
@serializer.load(:it_can_only_deserialize_strings)
53+
end
54+
end
55+
5056
test "raises ForbiddenClass when trying to serialize other data types" do
5157
assert_raises ActiveRecord::Encryption::Errors::ForbiddenClass do
5258
@serializer.dump("it can only serialize messages!")

guides/source/active_record_encryption.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ To encrypt Action Text fixtures, you should place them in `fixtures/action_text/
145145

146146
### Supported Types
147147

148-
`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.
148+
`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.
149149

150150
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:
151151

0 commit comments

Comments
 (0)