mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add #verified
and #valid_message?
to MessageVerifier
This commit adds a `#verified` method to `ActiveSupport::MessageVerifier` which will return either `false` when it encounters an error or the message. `#verify` continues to raise an `InvalidSignature` exception on error. This commit also adds a convenience boolean method on `MessageVerifier` as a way to check if a message is valid without performing the decoding.
This commit is contained in:
parent
cd77755ae4
commit
7ad541f955
3 changed files with 47 additions and 22 deletions
|
@ -1 +1,9 @@
|
|||
* Added `#verified` and `#valid_message?` methods to `ActiveSupport::MessageVerifier`
|
||||
|
||||
Previously, the only way to decode a message with `ActiveSupport::MessageVerifier` was to use `#verify`, which would raise an exception on invalid messages. Now, `#verified` will return either `false` when it encounters an error or the message.
|
||||
|
||||
Previously, there was no way to check if a message's format was valid without attempting to decode it. `#valid_message?` is a boolean convenience method that checks whether the message is valid without actually decoding it.
|
||||
|
||||
*Logan Leger*
|
||||
|
||||
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activesupport/CHANGELOG.md) for previous changes.
|
||||
|
|
|
@ -34,21 +34,30 @@ module ActiveSupport
|
|||
@serializer = options[:serializer] || Marshal
|
||||
end
|
||||
|
||||
def verify(signed_message)
|
||||
raise InvalidSignature if signed_message.blank?
|
||||
|
||||
def valid_message?(signed_message)
|
||||
return false if signed_message.blank?
|
||||
|
||||
data, digest = signed_message.split("--")
|
||||
if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
|
||||
data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
|
||||
end
|
||||
|
||||
def verified(signed_message)
|
||||
if valid_message?(signed_message)
|
||||
begin
|
||||
data = signed_message.split("--")[0]
|
||||
@serializer.load(decode(data))
|
||||
rescue ArgumentError => argument_error
|
||||
raise InvalidSignature if argument_error.message =~ %r{invalid base64}
|
||||
return false if argument_error.message =~ %r{invalid base64}
|
||||
raise
|
||||
end
|
||||
else
|
||||
raise InvalidSignature
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def verify(signed_message)
|
||||
verified(signed_message) || raise(InvalidSignature)
|
||||
end
|
||||
|
||||
def generate(value)
|
||||
data = encode(@serializer.dump(value))
|
||||
|
|
|
@ -27,21 +27,29 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
@data = { :some => "data", :now => Time.local(2010) }
|
||||
end
|
||||
|
||||
def test_valid_message
|
||||
data, hash = @verifier.generate(@data).split("--")
|
||||
assert !@verifier.valid_message?(nil)
|
||||
assert !@verifier.valid_message?("")
|
||||
assert !@verifier.valid_message?("#{data.reverse}--#{hash}")
|
||||
assert !@verifier.valid_message?("#{data}--#{hash.reverse}")
|
||||
assert !@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_missing_signature_raises
|
||||
assert_not_verified(nil)
|
||||
assert_not_verified("")
|
||||
|
||||
def test_verified_returns_false_on_invalid_message
|
||||
assert !@verifier.verified("purejunk")
|
||||
end
|
||||
|
||||
def test_tampered_data_raises
|
||||
data, hash = @verifier.generate(@data).split("--")
|
||||
assert_not_verified("#{data.reverse}--#{hash}")
|
||||
assert_not_verified("#{data}--#{hash.reverse}")
|
||||
assert_not_verified("purejunk")
|
||||
|
||||
def test_verify_exception_on_invalid_message
|
||||
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
|
||||
@verifier.verify("purejunk")
|
||||
end
|
||||
end
|
||||
|
||||
def test_alternative_serialization_method
|
||||
|
@ -50,6 +58,7 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
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
|
||||
|
@ -62,6 +71,11 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
# valid_message = @verifier.generate(foo: AutoloadClass.new('foo'))
|
||||
#
|
||||
valid_message = "BAh7BjoIZm9vbzonTWVzc2FnZVZlcmlmaWVyVGVzdDo6QXV0b2xvYWRDbGFzcwY6CUBmb29JIghmb28GOgZFVA==--f3ef39a5241c365083770566dc7a9eb5d6ace914"
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verified(valid_message)
|
||||
end
|
||||
assert_includes ["uninitialized constant MessageVerifierTest::AutoloadClass",
|
||||
"undefined class/module MessageVerifierTest::AutoloadClass"], exception.message
|
||||
exception = assert_raise(ArgumentError, NameError) do
|
||||
@verifier.verify(valid_message)
|
||||
end
|
||||
|
@ -75,12 +89,6 @@ class MessageVerifierTest < ActiveSupport::TestCase
|
|||
end
|
||||
assert_equal exception.message, 'Secret should not be nil.'
|
||||
end
|
||||
|
||||
def assert_not_verified(message)
|
||||
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
|
||||
@verifier.verify(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue