From 3f1d04e3bb04d240792112b5b45a9062c72e0df4 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 14 Sep 2014 19:34:17 -0700 Subject: Fix string/gid collision in job arguments Serialize Global IDs as special objects, distinguishable from Strings --- activejob/lib/active_job/arguments.rb | 30 +++++++++++++++++++--- .../test/cases/argument_serialization_test.rb | 14 +++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index 099ea4d169..86b85ebdba 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -35,12 +35,15 @@ module ActiveJob end private + GLOBALID_KEY = '_aj_globalid'.freeze + private_constant :GLOBALID_KEY + def serialize_argument(argument) case argument when *TYPE_WHITELIST argument when GlobalID::Identification - argument.to_global_id.to_s + { GLOBALID_KEY => argument.to_global_id.to_s } when Array argument.map { |arg| serialize_argument(arg) } when Hash @@ -61,16 +64,37 @@ module ActiveJob when Array argument.map { |arg| deserialize_argument(arg) } when Hash - argument.each_with_object({}.with_indifferent_access) do |(key, value), hash| - hash[key] = deserialize_argument(value) + if serialized_global_id?(argument) + deserialize_global_id argument + else + deserialize_hash argument end else raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}" end end + def serialized_global_id?(hash) + hash.size == 1 and hash.include?(GLOBALID_KEY) + end + + def deserialize_global_id(hash) + GlobalID::Locator.locate hash[GLOBALID_KEY] + end + + def deserialize_hash(serialized_hash) + serialized_hash.each_with_object({}.with_indifferent_access) do |(key, value), hash| + hash[key] = deserialize_argument(value) + end + end + + RESERVED_KEYS = [GLOBALID_KEY, GLOBALID_KEY.to_sym] + private_constant :RESERVED_KEYS + def serialize_hash_key(key) case key + when *RESERVED_KEYS + raise SerializationError.new("Can't serialize a Hash with reserved key #{key.inspect}") when String, Symbol key.to_s else diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb index 5a46c5cdef..dbe36fc572 100644 --- a/activejob/test/cases/argument_serialization_test.rb +++ b/activejob/test/cases/argument_serialization_test.rb @@ -31,12 +31,12 @@ class ArgumentSerializationTest < ActiveSupport::TestCase end test 'should convert records to Global IDs' do - assert_arguments_roundtrip [@person], [@person.to_gid.to_s] + assert_arguments_roundtrip [@person], ['_aj_globalid' => @person.to_gid.to_s] end test 'should dive deep into arrays and hashes' do - assert_arguments_roundtrip [3, [@person]], [3, [@person.to_gid.to_s]] - assert_arguments_roundtrip [{ 'a' => @person }], [{ 'a' => @person.to_gid.to_s }.with_indifferent_access] + assert_arguments_roundtrip [3, [@person]], [3, ['_aj_globalid' => @person.to_gid.to_s]] + assert_arguments_roundtrip [{ 'a' => @person }], [{ 'a' => { '_aj_globalid' => @person.to_gid.to_s }}.with_indifferent_access] end test 'should stringify symbol hash keys' do @@ -51,6 +51,14 @@ class ArgumentSerializationTest < ActiveSupport::TestCase assert_raises ActiveJob::SerializationError do ActiveJob::Arguments.serialize [ { :a => [{ 2 => 3 }] } ] end + + assert_raises ActiveJob::SerializationError do + ActiveJob::Arguments.serialize [ '_aj_globalid' => 1 ] + end + + assert_raises ActiveJob::SerializationError do + ActiveJob::Arguments.serialize [ :_aj_globalid => 1 ] + end end test 'should not allow non-primitive objects' do -- cgit v1.2.3