Add new methods to MessageVerifier #17727
Conversation
Thanks for your contribution. Some questions:
Overall, perhaps an API like this: def valid_format?(message)
# true or false
end
def verified?(message)
# true or false
end
def verify(message)
verified?(message) || raise(...)
end The above will address the ambiguous meaning of |
Good questions.
|
@lleger thanks, I update my first comment with proposed example. |
I'm not sure about |
@lleger You mention this in response to the breaking change:
We do not allow breaking API changes, even in major version bumps. We follow a "shifted" semver model. Major and minor versions are treated as the same, with major version bumps reserved for features deemed "major enough". In either case, all breaking API changes must go through a deprecation cycle. Even if we had a deprecation warning emitted with the exception for a version, I don't think we would accept a change that could potentially silently introduce a vulnerability like that. |
@sgrif Gotcha. So would the appropriate direction here be to leave |
Yes, that would be the safer course of action in this case. |
|
|
||
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. | ||
|
||
*Logan Leger* |
sgrif
Nov 26, 2014
Contributor
You can merge these into a single entry
You can merge these into a single entry
lleger
Nov 26, 2014
Author
Contributor
Alright, I'll update this.
Alright, I'll update this.
data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data)) | ||
end | ||
|
||
def verified?(signed_message) |
sgrif
Nov 26, 2014
Contributor
Does this make sense to be a predicate, since it has a return value?
Does this make sense to be a predicate, since it has a return value?
lleger
Nov 26, 2014
Author
Contributor
Yeah, I was actually thinking about this last night. I don't think it does. I'm not sure why I wrote it as such. I'll change it.
Yeah, I was actually thinking about this last night. I don't think it does. I'm not sure why I wrote it as such. I'll change it.
end | ||
end | ||
|
||
def verify(signed_message) | ||
verified?(signed_message) || raise(InvalidSignature) |
sgrif
Nov 26, 2014
Contributor
Would the diff be cleaner if the verify
method was untouched, and we rescued the exception in the return false version instead? (Similar to how save works)
Would the diff be cleaner if the verify
method was untouched, and we rescued the exception in the return false version instead? (Similar to how save works)
lleger
Nov 26, 2014
Author
Contributor
Indeed I suppose it would. Would that be preferred?
Indeed I suppose it would. Would that be preferred?
@sgrif I updated the PR to reduce the CHANGELOG entries and I also changed the |
I think that having a raising method implemented in terms of a non raising method is logically simpler than the non raising method rescuing from a raising method. Eugene
|
I agree that the raising implemented in terms of the non raising makes more sense. The complexity of the fit history, and being able to quickly find the commit which introduced a line of code is important to us, though. I really don't feel strongly on it though so you should go with whatever you think is best. |
Understandable. I could, perhaps, reorder the methods and reduce the diff noise a bit, but that seems gratuitous to me. It seems logical how it's structured. The overall diff is pretty small, too.
In that case, I think this is ready to be merged, yes? Travis is still choking, but these tests run fine locally (the failure looks like a Ruby interpreter bug and not related to these changes). |
raise | ||
end | ||
else | ||
raise InvalidSignature | ||
false |
egilburg
Nov 26, 2014
Contributor
IMO a side-effect-free pattern which intends to whitelist an object against a condition, and which returns an object on success condition, should return nil
rather than false
on failure condition. This is similar to the presence
method in Active Support.
You can just not have the else
clause here and it'll return nil
implicitly.
IMO a side-effect-free pattern which intends to whitelist an object against a condition, and which returns an object on success condition, should return nil
rather than false
on failure condition. This is similar to the presence
method in Active Support.
You can just not have the else
clause here and it'll return nil
implicitly.
lleger
Nov 27, 2014
Author
Contributor
Hm, yeah, I see what you're saying here. I think it works better, in my mind, returning false
. I imagined this method working more like #save
et al. Besides removing a branch in the conditional, is there any other benefit to returning nil
implicitly over returning false
explicitly? I guess, practically, it shouldn't largely affect the functionality.
Hm, yeah, I see what you're saying here. I think it works better, in my mind, returning false
. I imagined this method working more like #save
et al. Besides removing a branch in the conditional, is there any other benefit to returning nil
implicitly over returning false
explicitly? I guess, practically, it shouldn't largely affect the functionality.
rafaelfranca
Dec 2, 2014
Member
It is the project guideline to not return explicit booleans. We prefer to always return nil
unless it is documented to return false
It is the project guideline to not return explicit booleans. We prefer to always return nil
unless it is documented to return false
lleger
Dec 2, 2014
Author
Contributor
Sorry, I wasn't aware of that guideline. (Are those guidelines written down anywhere? Perhaps I just missed it.) I prefer the explicit false
, but if it's the guideline, I'm happy to change it, since it makes no practical difference. Would you like me to open another PR with that change?
Sorry, I wasn't aware of that guideline. (Are those guidelines written down anywhere? Perhaps I just missed it.) I prefer the explicit false
, but if it's the guideline, I'm happy to change it, since it makes no practical difference. Would you like me to open another PR with that change?
sgrif
Dec 2, 2014
Contributor
It was already changed. :)
On Tue Dec 02 2014 at 9:32:27 AM Logan Leger notifications@github.com
wrote:
In activesupport/lib/active_support/message_verifier.rb:
raise
end
else
-
raise InvalidSignature
-
false
Sorry, I wasn't aware of that guideline. (Are those guidelines written
down anywhere? Perhaps I just missed it.) I prefer the explicit false,
but if it's the guideline, I'm happy to change it, since it makes no
practical difference. Would you like me to open another PR with that change?
—
Reply to this email directly or view it on GitHub
https://github.com/rails/rails/pull/17727/files#r21170473.
It was already changed. :)
On Tue Dec 02 2014 at 9:32:27 AM Logan Leger notifications@github.com
wrote:
In activesupport/lib/active_support/message_verifier.rb:
raise end else
raise InvalidSignature
false
Sorry, I wasn't aware of that guideline. (Are those guidelines written
down anywhere? Perhaps I just missed it.) I prefer the explicit false,
but if it's the guideline, I'm happy to change it, since it makes no
practical difference. Would you like me to open another PR with that change?—
Reply to this email directly or view it on GitHub
https://github.com/rails/rails/pull/17727/files#r21170473.
rafaelfranca
Dec 2, 2014
Member
Yes. It is written at http://guides.rubyonrails.org/api_documentation_guidelines.html#booleans
Yes. It is written at http://guides.rubyonrails.org/api_documentation_guidelines.html#booleans
lleger
Dec 2, 2014
Author
Contributor
It was already changed. :)
Oh, I see. @rafaelfranca changed it in bc8cc56. Great.
Yes. It is written at…
Oh, I see now. Thanks for pointing that out, I must've missed it. I'll know to do it next time!
It was already changed. :)
Oh, I see. @rafaelfranca changed it in bc8cc56. Great.
Yes. It is written at…
Oh, I see now. Thanks for pointing that out, I must've missed it. I'll know to do it next time!
Yeah, we'll just need the commits squashed and rebased. I also can't merge until we ship 4.2.0.rc1, which is when we'll create the |
I've been keeping it rebased against master. I'll go ahead and squash it down to one commit and push that up.
Indeed, I suppose it is. I'll make sure to follow up here when the RC drops. Thanks for all your help! |
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.
Great thanks. I need to let ci run I'll check on this in the morning |
…erifier Add new methods to MessageVerifier
@lleger I am trying to add documentation to the methods that you added, as suggested by @rafaelfranca in ee73d9f. However, I have a doubt regarding
However, that does not seem to be true: Am I right? And if that's the case… what is the correct purpose of |
@claudiob Actually, it no longer returns Anyway, I was going to write the documentation, but hadn't gotten around to it yet. Thanks for tackling it! |
@lleger Thanks for your reply. Let me clarify what I was asking for with an example. require 'yaml'
verifier1 = ActiveSupport::MessageVerifier.new 's3Krit'
verifier2 = ActiveSupport::MessageVerifier.new 's3Krit', serializer: YAML If you sign a message with the first verifier, and then you ask if the message is valid for the second verifier, you will get signed_message = verifier1.generate 'private-message' #=> "BAhJI..."
verifier2.valid_message?(signed_message) #=> true This is my question for you: don't you think this is misleading? As I developer, I see that verifier2.verified(signed_message) # Psych::SyntaxError: (<unknown>): control characters are not allowed at line 1 column 1
verifier2.verify(signed_message) # Psych::SyntaxError: (<unknown>): control characters are not allowed at line 1 column 1 What do you think? Did you envision |
@claudiob Ah, I see what you're saying now. I don't consider this misleading. I intended for |
@lleger what I don't understand is a use case of what you just described:
As I tried to explain with the code above, you can call Then… why not call |
@claudiob For example, if you encode a token to send in an email, and you want to check if that token is valid before showing the page; if not, you want to redirect. At that point I don't care about the message itself, I just care that it's valid digest. I don't actually care if the message itself is valid until I go through the steps of validating the message (maybe from the database, maybe in a different action or controller, even). If you wanted to, you could easily call |
@lleger Is this the case you are describing?
|
@claudiob More or less, yes. |
[ci skip] Complements rails#17727 and closes ee73d9f. @lleger How do you feel about this?
[ci skip] As confirmed by @lleger (the author of `verified`) [in this comment](rails#17727 (comment)): > Actually, it no longer returns false explicitly (bc8cc56), so I guess the CHANGELOG isn't totally accurate. It returns nil instead (but the functionality isn't practically different).
[ci skip] Complements rails#17727 and closes ee73d9f. @lleger How do you feel about this?
[ci skip] As confirmed by @lleger (the author of `verified`) [in this comment](rails#17727 (comment)): > Actually, it no longer returns false explicitly (bc8cc56), so I guess the CHANGELOG isn't totally accurate. It returns nil instead (but the functionality isn't practically different).
This commit adds two boolean methods to
ActiveSupport::MessageVerifier
:#verified
, to returnfalse
on invalid messages, and#valid_message?
, a convenience boolean method to check the validity of a message's format.#verified
methodThe first commit adds a
#verified
method to return false when an error occurs decoding the message; it continues to return the message on success. (The behavior of#verify
remains unchanged.) Previously it was not possible to attempt to decode a message without possibly encountering an exception.Example
#valid_message?
methodSometimes it may be beneficial to verify the format of the message without performing the decoding. The second commit adds a
#valid_message?
boolean method which performs this validation. It has the added benefit of cleaning up a conditional in the#verified
method. I also took some liberty restructuring the tests in this commit.Example