From 48ab52d75567c230165f254471bbeacc31568b29 Mon Sep 17 00:00:00 2001 From: maximerety Date: Thu, 10 Aug 2023 16:37:04 +0200 Subject: [PATCH] [Fix #48922] Use context from the encrypted attribute type in encrypted_attribute? Also fix implementation of TestEncryptor#encrypted? The assertions fail because encrypted_attribute? delegates a decryption attempt to the default encryptor (instead of the one configured) to check if the value is actually encrypted. --- activerecord/CHANGELOG.md | 4 ++++ .../lib/active_record/encryption/encryptable_record.rb | 8 +++++++- .../active_record/encryption/encrypted_attribute_type.rb | 4 ++++ .../test/cases/encryption/encryption_schemes_test.rb | 8 +++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 10d5417cb57cd..c8f10a69e16a3 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Fix `encrypted_attribute?` to take into account context properties passed to `encrypts`. + + *Maxime Réty* + * Add `explain` support for methods like `last`, `pluck` and `count` Let `explain` return a proxy that delegates these methods: diff --git a/activerecord/lib/active_record/encryption/encryptable_record.rb b/activerecord/lib/active_record/encryption/encryptable_record.rb index 34607a497633b..35fae4623c80f 100644 --- a/activerecord/lib/active_record/encryption/encryptable_record.rb +++ b/activerecord/lib/active_record/encryption/encryptable_record.rb @@ -144,7 +144,13 @@ def validate_column_size(attribute_name) # Returns whether a given attribute is encrypted or not. def encrypted_attribute?(attribute_name) - ActiveRecord::Encryption.encryptor.encrypted? read_attribute_before_type_cast(attribute_name) + name = attribute_name.to_s + name = self.class.attribute_aliases[name] || name + + return false unless self.class.encrypted_attributes&.include? name.to_sym + + type = type_for_attribute(name) + type.encrypted? read_attribute_before_type_cast(name) end # Returns the ciphertext for +attribute_name+. diff --git a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb index 7fd792895bfbe..3c8a5834d4dc2 100644 --- a/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb +++ b/activerecord/lib/active_record/encryption/encrypted_attribute_type.rb @@ -44,6 +44,10 @@ def serialize(value) end end + def encrypted?(value) + with_context { encryptor.encrypted? value } + end + def changed_in_place?(raw_old_value, new_value) old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value) old_value != new_value diff --git a/activerecord/test/cases/encryption/encryption_schemes_test.rb b/activerecord/test/cases/encryption/encryption_schemes_test.rb index c92b366e4be09..88a476af561ca 100644 --- a/activerecord/test/cases/encryption/encryption_schemes_test.rb +++ b/activerecord/test/cases/encryption/encryption_schemes_test.rb @@ -9,6 +9,7 @@ class ActiveRecord::Encryption::EncryptionSchemesTest < ActiveRecord::Encryption author = create_author_with_name_encrypted_with_previous_scheme assert_equal "dhh", author.reload.name + assert author.encrypted_attribute? :name end test "when defining previous encryption schemes, you still get Decryption errors when using invalid clear values" do @@ -24,6 +25,7 @@ class ActiveRecord::Encryption::EncryptionSchemesTest < ActiveRecord::Encryption test "use a custom encryptor" do author = EncryptedAuthor1.create name: "1" assert_equal "1", author.name + assert author.encrypted_attribute? :name end test "support previous contexts" do @@ -32,10 +34,12 @@ class ActiveRecord::Encryption::EncryptionSchemesTest < ActiveRecord::Encryption author = EncryptedAuthor2.create name: "2" assert_equal "2", author.name assert_equal author, EncryptedAuthor2.find_by_name("2") + assert author.encrypted_attribute? :name Author.find(author.id).update! name: "1" assert_equal "1", author.reload.name assert_equal author, EncryptedAuthor2.find_by_name("1") + assert_not author.encrypted_attribute? :name end test "use global previous schemes to decrypt data encrypted with previous schemes" do @@ -191,7 +195,9 @@ def decrypt(encrypted_text, key_provider: nil, cipher_options: {}) end def encrypted?(text) - text == encrypted_text + decrypt(text) + rescue ActiveRecord::Encryption::Errors::Decryption + false end end