Skip to content

Commit 6f79f25

Browse files
djmbrafaelfranca
andauthored
Decryption casting v2 revert (rails#52693)
* Revert "Switch back to DelegateClass(ActiveModel::Type::Value)" This reverts commit 91745df. * Revert "Update docs and changelog" This reverts commit b234a94. * Revert "Nest encrypted attribute types within serialized types" This reverts commit 6a27e7b. * Revert "Encryption casting with `encrypts` before `serialize`" This reverts commit 4dd2b22. Co-authored-by: Rafael Mendonça França <[email protected]>
1 parent b6fcd66 commit 6f79f25

File tree

8 files changed

+45
-62
lines changed

8 files changed

+45
-62
lines changed

activerecord/CHANGELOG.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,6 @@
1212

1313
*Justin Talbott*
1414

15-
* Deserialize database values before decryption
16-
17-
PostgreSQL binary values (`ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea`)
18-
need to be deserialized before they are decrypted.
19-
20-
Additionally ensure that the order of serialization/deserialization is consistent
21-
for `serialize :foo` and `encrypts :foo` whichever order they are declared in.
22-
23-
*Donal McBreen*
24-
2515
* Infer default `:inverse_of` option for `delegated_type` definitions.
2616

2717
```ruby

activerecord/lib/active_record/encryption/encryptable_record.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,7 @@ def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, s
8888
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
8989
downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
9090

91-
type_options = { scheme: scheme, default: columns_hash[name.to_s]&.default }
92-
93-
if cast_type.serialized?
94-
cast_type.replace_serialized_subtype do |current_subtype|
95-
ActiveRecord::Encryption::EncryptedAttributeType.new(cast_type: current_subtype, **type_options)
96-
end
97-
else
98-
ActiveRecord::Encryption::EncryptedAttributeType.new(cast_type: cast_type, **type_options)
99-
end
91+
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
10092
end
10193

10294
preserve_original_encrypted(name) if ignore_case

activerecord/lib/active_record/encryption/encrypted_attribute_type.rb

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def cast(value)
3333
end
3434

3535
def deserialize(value)
36-
decrypt(cast_type.deserialize(value))
36+
cast_type.deserialize decrypt(value)
3737
end
3838

3939
def serialize(value)
@@ -81,7 +81,7 @@ def previous_type?
8181
@previous_type
8282
end
8383

84-
def decrypt(value)
84+
def decrypt_as_text(value)
8585
with_context do
8686
unless value.nil?
8787
if @default && @default == value
@@ -99,6 +99,10 @@ def decrypt(value)
9999
end
100100
end
101101

102+
def decrypt(value)
103+
text_to_database_type decrypt_as_text(value)
104+
end
105+
102106
def try_to_deserialize_with_previous_encrypted_types(value)
103107
previous_types.each.with_index do |type, index|
104108
break type.deserialize(value)
@@ -124,11 +128,12 @@ def serialize_with_oldest(value)
124128
end
125129

126130
def serialize_with_current(value)
127-
value = value&.downcase if downcase?
128-
cast_type.serialize(encrypt(value.to_s)) unless value.nil?
131+
casted_value = cast_type.serialize(value)
132+
casted_value = casted_value&.downcase if downcase?
133+
encrypt(casted_value.to_s) unless casted_value.nil?
129134
end
130135

131-
def encrypt(value)
136+
def encrypt_as_text(value)
132137
with_context do
133138
if encryptor.binary? && !cast_type.binary?
134139
raise Errors::Encoding, "Binary encoded data can only be stored in binary columns"
@@ -138,6 +143,10 @@ def encrypt(value)
138143
end
139144
end
140145

146+
def encrypt(value)
147+
text_to_database_type encrypt_as_text(value)
148+
end
149+
141150
def encryptor
142151
ActiveRecord::Encryption.encryptor
143152
end
@@ -153,6 +162,14 @@ def decryption_options
153162
def clean_text_scheme
154163
@clean_text_scheme ||= ActiveRecord::Encryption::Scheme.new(downcase: downcase?, encryptor: ActiveRecord::Encryption::NullEncryptor.new)
155164
end
165+
166+
def text_to_database_type(value)
167+
if value && cast_type.binary?
168+
ActiveModel::Type::Binary::Data.new(value)
169+
else
170+
value
171+
end
172+
end
156173
end
157174
end
158175
end

activerecord/lib/active_record/type/serialized.rb

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ def initialize(subtype, coder)
1515
super(subtype)
1616
end
1717

18-
def init_with(coder) # :nodoc:
19-
# Ensures YAML deserialization calls __setobj__
20-
@subtype = coder["subtype"]
21-
@coder = coder["coder"]
22-
__setobj__(subtype)
23-
end
24-
2518
def deserialize(value)
2619
if default_value?(value)
2720
value
@@ -64,12 +57,6 @@ def serialized? # :nodoc:
6457
true
6558
end
6659

67-
def replace_serialized_subtype(&block) # :nodoc:
68-
@subtype = block.call(subtype)
69-
__setobj__(@subtype)
70-
self
71-
end
72-
7360
private
7461
def default_value?(value)
7562
value == coder.load(nil)

activerecord/test/cases/encryption/encryptable_record_message_pack_serialized_test.rb

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,19 @@
55
require "models/book_encrypted"
66
require "active_record/encryption/message_pack_message_serializer"
77

8-
class ActiveRecord::Encryption::EncryptableRecordMessagePackSerializedTest < ActiveRecord::EncryptionTestCase
8+
class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase
99
fixtures :encrypted_books
1010

1111
test "binary data can be serialized with message pack" do
1212
all_bytes = (0..255).map(&:chr).join
13-
book = EncryptedBookWithBinaryMessagePackSerialized.create!(logo: all_bytes)
14-
assert_encrypted_attribute(book, :logo, all_bytes)
13+
assert_equal all_bytes, EncryptedBookWithBinaryMessagePackSerialized.create!(logo: all_bytes).logo
1514
end
1615

1716
test "binary data can be encrypted uncompressed and serialized with message pack" do
18-
# Strings below 140 bytes are not compressed
1917
low_bytes = (0..127).map(&:chr).join
2018
high_bytes = (128..255).map(&:chr).join
21-
22-
assert_encrypted_attribute(EncryptedBookWithBinaryMessagePackSerialized.create!(logo: low_bytes), :logo, low_bytes)
23-
assert_encrypted_attribute(EncryptedBookWithBinaryMessagePackSerialized.create!(logo: high_bytes), :logo, high_bytes)
19+
assert_equal low_bytes, EncryptedBookWithBinaryMessagePackSerialized.create!(logo: low_bytes).logo
20+
assert_equal high_bytes, EncryptedBookWithBinaryMessagePackSerialized.create!(logo: high_bytes).logo
2421
end
2522

2623
test "text columns cannot be serialized with message pack" do

activerecord/test/cases/encryption/encryptable_record_test.rb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,6 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption
9292
assert_encrypted_attribute(traffic_light, :state, states)
9393
end
9494

95-
test "encrypts serialized attributes where encrypts is declared first" do
96-
states = ["green", "red"]
97-
traffic_light = EncryptedFirstTrafficLight.create!(state: states, long_state: states)
98-
assert_encrypted_attribute(traffic_light, :state, states)
99-
end
100-
10195
test "encrypts store attributes with accessors" do
10296
traffic_light = EncryptedTrafficLightWithStoreState.create!(color: "red", long_state: ["green", "red"])
10397
assert_equal "red", traffic_light.color
@@ -410,13 +404,13 @@ def name
410404
test "binary data can be encrypted uncompressed" do
411405
low_bytes = (0..127).map(&:chr).join
412406
high_bytes = (128..255).map(&:chr).join
413-
assert_encrypted_attribute EncryptedBookWithBinary.create!(logo: low_bytes), :logo, low_bytes
414-
assert_encrypted_attribute EncryptedBookWithBinary.create!(logo: high_bytes), :logo, high_bytes
407+
assert_equal low_bytes, EncryptedBookWithBinary.create!(logo: low_bytes).logo
408+
assert_equal high_bytes, EncryptedBookWithBinary.create!(logo: high_bytes).logo
415409
end
416410

417411
test "serialized binary data can be encrypted" do
418412
json_bytes = (32..127).map(&:chr)
419-
assert_encrypted_attribute EncryptedBookWithSerializedBinary.create!(logo: json_bytes), :logo, json_bytes
413+
assert_equal json_bytes, EncryptedBookWithSerializedBinary.create!(logo: json_bytes).logo
420414
end
421415

422416
test "can compress data with custom compressor" do

activerecord/test/models/traffic_light_encrypted.rb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@ class EncryptedTrafficLight < TrafficLight
77
encrypts :state
88
end
99

10-
class EncryptedFirstTrafficLight < ActiveRecord::Base
11-
self.table_name = "traffic_lights"
12-
13-
encrypts :state
14-
serialize :state, type: Array
15-
serialize :long_state, type: Array
16-
end
17-
1810
class EncryptedTrafficLightWithStoreState < TrafficLight
1911
store :state, accessors: %i[ color ], coder: ActiveRecord::Coders::JSON
2012
encrypts :state

guides/source/active_record_encryption.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,21 @@ To encrypt Action Text fixtures, you should place them in `fixtures/action_text/
147147

148148
`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

150-
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).
150+
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:
151+
152+
```ruby
153+
# CORRECT
154+
class Article < ApplicationRecord
155+
serialize :title, type: Title
156+
encrypts :title
157+
end
158+
159+
# INCORRECT
160+
class Article < ApplicationRecord
161+
encrypts :title
162+
serialize :title, type: Title
163+
end
164+
```
151165

152166
### Ignoring Case
153167

0 commit comments

Comments
 (0)