Skip to content

Commit

Permalink
Nest encrypted attribute types within serialized types
Browse files Browse the repository at this point in the history
Make the order in which `serializes :foo` and `encrypts :foo` are called
irrelevant by always nesting the encrypted attribute type inside the
serialized type.

This required switching from `DelegateClass` to `SimpleDelegator` so
that object we are delegating to can be replaced.

To ensure that the serialized type survives YAML serialization (there's
a test for this), we need to implement init_with to call __setobj__.
  • Loading branch information
djmb committed Aug 20, 2024
1 parent 1120184 commit 64c60ff
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 3 deletions.
10 changes: 9 additions & 1 deletion activerecord/lib/active_record/encryption/encryptable_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,15 @@ def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, s
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties

ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
if cast_type.serialized?
cast_type.tap do |serialized_type|
serialized_type.replace_serialized_subtype do |current_subtype|
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: current_subtype, default: columns_hash[name.to_s]&.default)
end
end
else
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
end
end

preserve_original_encrypted(name) if ignore_case
Expand Down
16 changes: 14 additions & 2 deletions activerecord/lib/active_record/type/serialized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module ActiveRecord
module Type
class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
class Serialized < SimpleDelegator # :nodoc:
undef to_yaml if method_defined?(:to_yaml)

include ActiveModel::Type::Helpers::Mutable
Expand All @@ -12,7 +12,14 @@ class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
def initialize(subtype, coder)
@subtype = subtype
@coder = coder
super(subtype)
__setobj__(subtype)
end

def init_with(coder) # :nodoc:
# Ensures YAML deserialization calls __setobj__
@subtype = coder["subtype"]
@coder = coder["coder"]
__setobj__(subtype)
end

def deserialize(value)
Expand Down Expand Up @@ -57,6 +64,11 @@ def serialized? # :nodoc:
true
end

def replace_serialized_subtype(&block) # :nodoc:
@subtype = block.call(subtype)
__setobj__(@subtype)
end

private
def default_value?(value)
value == coder.load(nil)
Expand Down

0 comments on commit 64c60ff

Please sign in to comment.