-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Check if the record is persisted in update_tracked_fields!
#4796
Conversation
In some cases, invalid records could be created during the signup process because we were calling `save(validate: false)` inside the `update_tracked_fields!` method. See #4673 for more information. This was fixed on #4674 by calling `save` directly, but it caused some trouble and confusion since it changed Devise's behavior significantly. We talked about on #4790 and it doesn't even make sense to call `save` on an object that isn't persisted yet, so I've added a guard clause to the `update_tracked_fields!` method. Fixes #4790
# `save` here because invalid users can be saved if we don't. | ||
# See https://github.com/plataformatec/devise/issues/4673 for more details. | ||
return if new_record? | ||
|
||
update_tracked_fields(request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not the update_tracked_fields
still be called when it is a new record?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the signup succeeds the record would already be persisted here. This is being called from a Warden's after_set_user
callback. The callback is triggered when we call set_user
from the sign_in
helper, which is called after the record is created in registrations controller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Is there any way we can test this to make sure it doesn't happen anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test introduced in https://github.com/plataformatec/devise/pull/4674/files#diff-8aa58f219abef0280c8857f9161c087bR42 is still passing, so I think we're good to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But how about the tests to make sure that every request don't run validations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I'm not sure of how we should test this 🤔
Do you think we should check here if the validations were run or in the sessions controller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way to test this is write an integration test, add a validation to the model that sets a global value in the class if the validation is run. This way we can test if a request to any action sets this global value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth to write a unit test to the Trackable
module to check if the record is saved not running the validations 🤔 .
test 'sign in should not run model validations' do | ||
sign_in_as_user | ||
|
||
refute $validations_performed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I said a global I was thinking more in a class variable inside the model
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get it now 😅
I've changed to use a class variable
3dd45e9
to
fb61222
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💪
test/integration/trackable_test.rb
Outdated
@@ -3,6 +3,12 @@ | |||
require 'test_helper' | |||
|
|||
class TrackableHooksTest < Devise::IntegrationTest | |||
test "sign in with HTTP should not run model validations" do | |||
create_user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is create_user
already called internally when we use sign_in_as_user
method? 🤔
fb61222
to
c24e754
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason, this is necessary now. It seems to be ultimately caused by this change heartcombo/devise#4796 which stopped validating the user on save after updating the sign-in tracking fields. With that change in place, after calling `lookup_user.update!` earlier in this method the updated values - including the `provider` and `uid` - stick around on the user object that's in memory. Manually reloading the user object after a failed update resolves this.
Hey folks, taking a look at this, I'm not sure I see how/why a record would get to this call via a warden hook while not being persisted. This must be something custom someone is doing along the way, since as you pointed out @tegon, the registrations controller calls sign up which will in turn delegate to warden's set_user eventually, but it calls devise/app/controllers/devise/registrations_controller.rb Lines 19 to 24 in e8e0c27
So, unless we're talking about some custom code in the registration that's not calling save and/or checking for the errors before calling If I remove that check, only the model tests fail, the integration ones don't, which is more proof that the workflow as a whole is working I think. |
@carlosantoniodasilva It has been a long time, but I think you're right - it can't happen with Devise's default setup. If I recall correctly, we ended up adding this patch because it was affecting multiple applications out there. I believe this could happen if the apps had other validations on their models apart from the Devise ones. Since the trackable module runs apart from registrations, that's the way we prevented it from happening in there. |
Makes sense. I still think the contract should be that you're not running the hooks for an unsaved model, I'll see about that in a future major version or something. Thanks! |
In some cases, invalid records could be created during the signup process because we were calling
save(validate: false)
inside theupdate_tracked_fields!
method. See #4673 for more information.This was fixed on #4674 by calling
save
directly, but it caused some trouble and confusion since it changed Devise's behavior significantly.We talked about on #4790 and it doesn't even make sense to call
save
on an object that isn't persisted yet, so I've added a guard clause to theupdate_tracked_fields!
method.