9fbfd8100b
In #42843 and #42846, several config settings were added to control the default serializer for `MessageEncryptor` and `MessageVerifier`, and to provide a migration path from a default `Marshal` serializer to a default `JSON` serializer: * `config.active_support.default_message_encryptor_serializer` * Supports `:marshal`, `:hybrid`, or `:json`. * `config.active_support.default_message_verifier_serializer` * Supports `:marshal`, `:hybrid`, or `:json`. * `config.active_support.fallback_to_marshal_deserialization` * Affects `:hybrid` for both `MessageEncryptor` and `MessageVerifier`. * `config.active_support.use_marshal_serialization` * Affects `:hybrid` for both `MessageEncryptor` and `MessageVerifier`. This commit unifies those config settings into a single setting, `config.active_support.message_serializer`, which supports `:marshal`, `:json_allow_marshal`, and `:json` values. So, for example, ```ruby config.active_support.default_message_encryptor_serializer = :hybrid config.active_support.default_message_verifier_serializer = :hybrid config.active_support.fallback_to_marshal_deserialization = true config.active_support.use_marshal_serialization = false ``` becomes ```ruby config.active_support.message_serializer = :json_allow_marshal ``` and ```ruby config.active_support.default_message_encryptor_serializer = :hybrid config.active_support.default_message_verifier_serializer = :hybrid config.active_support.fallback_to_marshal_deserialization = false config.active_support.use_marshal_serialization = false ``` becomes ```ruby config.active_support.message_serializer = :json ``` This commit also replaces `ActiveSupport::JsonWithMarshalFallback` with `ActiveSupport::Messages::SerializerWithFallback`, which implements a generic mechanism for serializer fallback. The `:marshal` serializer uses this mechanism too, so ```ruby config.active_support.default_message_encryptor_serializer = :hybrid config.active_support.default_message_verifier_serializer = :hybrid config.active_support.fallback_to_marshal_deserialization = false config.active_support.use_marshal_serialization = true ``` becomes ```ruby config.active_support.message_serializer = :marshal ``` Additionally, the logging behavior of `JsonWithMarshalFallback` has been replaced with notifications which include the names of the intended and actual serializers, as well as the serialized and deserialized message data. This provides a more targeted means of tracking serializer fallback events. It also allows the user to "silence" such events, if desired, without an additional config setting. All of these changes make it easier to add migration paths for new serializers such as `ActiveSupport::MessagePack`.
117 lines
3.9 KiB
Ruby
117 lines
3.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "abstract_unit"
|
|
require "openssl"
|
|
require "active_support/time"
|
|
require "active_support/json"
|
|
require_relative "messages/message_codec_tests"
|
|
|
|
class MessageVerifierTest < ActiveSupport::TestCase
|
|
include MessageCodecTests
|
|
|
|
class JSONSerializer
|
|
def dump(value)
|
|
ActiveSupport::JSON.encode(value)
|
|
end
|
|
|
|
def load(value)
|
|
ActiveSupport::JSON.decode(value)
|
|
end
|
|
end
|
|
|
|
def setup
|
|
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
|
|
@data = { "some" => "data", "now" => Time.utc(2010) }
|
|
@secret = SecureRandom.random_bytes(32)
|
|
end
|
|
|
|
def test_valid_message
|
|
data, hash = @verifier.generate(@data).split("--")
|
|
assert_not @verifier.valid_message?(nil)
|
|
assert_not @verifier.valid_message?("")
|
|
assert_not @verifier.valid_message?("\xff") # invalid encoding
|
|
assert_not @verifier.valid_message?("#{data.reverse}--#{hash}")
|
|
assert_not @verifier.valid_message?("#{data}--#{hash.reverse}")
|
|
assert_not @verifier.valid_message?("purejunk")
|
|
end
|
|
|
|
def test_simple_round_tripping
|
|
message = @verifier.generate(@data)
|
|
assert_equal @data, @verifier.verified(message)
|
|
assert_equal @data, @verifier.verify(message)
|
|
end
|
|
|
|
def test_round_tripping_nil
|
|
message = @verifier.generate(nil)
|
|
assert_nil @verifier.verified(message)
|
|
assert_nil @verifier.verify(message)
|
|
end
|
|
|
|
def test_verified_returns_false_on_invalid_message
|
|
assert_not @verifier.verified("purejunk")
|
|
end
|
|
|
|
def test_verify_exception_on_invalid_message
|
|
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
|
|
@verifier.verify("purejunk")
|
|
end
|
|
end
|
|
|
|
test "supports URL-safe encoding" do
|
|
verifier = ActiveSupport::MessageVerifier.new(@secret, url_safe: true, serializer: JSON)
|
|
|
|
# To verify that the message payload uses a URL-safe encoding (i.e. does not
|
|
# use "+" or "/"), the unencoded bytes should have a 6-bit aligned
|
|
# occurrence of `0b111110` or `0b111111`. Also, to verify that the message
|
|
# payload is unpadded, the number of unencoded bytes should not be a
|
|
# multiple of 3.
|
|
#
|
|
# The JSON serializer adds quotes around strings, adding 1 byte before and
|
|
# 1 byte after the input string. So we choose an input string of "??",
|
|
# which is serialized as:
|
|
# 00100010 00111111 00111111 00100010
|
|
# Which is 6-bit aligned as:
|
|
# 001000 100011 111100 111111 001000 10xxxx
|
|
data = "??"
|
|
message = verifier.generate(data)
|
|
|
|
assert_equal data, verifier.verified(message)
|
|
assert_equal message, URI.encode_www_form_component(message)
|
|
assert_not_equal 0, message.rpartition("--").first.length % 4,
|
|
"Unable to assert that the message payload is unpadded, because it does not require padding"
|
|
end
|
|
|
|
def test_alternative_serialization_method
|
|
prev = ActiveSupport.use_standard_json_time_format
|
|
ActiveSupport.use_standard_json_time_format = true
|
|
verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", serializer: JSONSerializer.new)
|
|
message = verifier.generate({ :foo => 123, "bar" => Time.utc(2010) })
|
|
exp = { "foo" => 123, "bar" => "2010-01-01T00:00:00.000Z" }
|
|
assert_equal exp, verifier.verified(message)
|
|
assert_equal exp, verifier.verify(message)
|
|
ensure
|
|
ActiveSupport.use_standard_json_time_format = prev
|
|
end
|
|
|
|
def test_verify_with_parse_json_times
|
|
previous = [ ActiveSupport.parse_json_times, Time.zone ]
|
|
ActiveSupport.parse_json_times, Time.zone = true, "UTC"
|
|
|
|
assert_equal "hi", @verifier.verify(@verifier.generate("hi", expires_at: Time.now.utc + 10))
|
|
ensure
|
|
ActiveSupport.parse_json_times, Time.zone = previous
|
|
end
|
|
|
|
def test_raise_error_when_secret_is_nil
|
|
exception = assert_raise(ArgumentError) do
|
|
ActiveSupport::MessageVerifier.new(nil)
|
|
end
|
|
assert_equal "Secret should not be nil.", exception.message
|
|
end
|
|
|
|
private
|
|
def make_codec(**options)
|
|
ActiveSupport::MessageVerifier.new(@secret, **options)
|
|
end
|
|
end
|