Skip to content
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

Merged
merged 2 commits into from
Mar 14, 2018

Conversation

tegon
Copy link
Member

@tegon tegon commented Mar 5, 2018

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.

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
@tegon tegon self-assigned this Mar 5, 2018
# `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)
Copy link
Collaborator

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?

Copy link
Member Author

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.

Copy link
Collaborator

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?

Copy link
Member Author

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.

Copy link
Collaborator

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?

Copy link
Member Author

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?

Copy link
Collaborator

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.

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
Copy link
Collaborator

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

Copy link
Member Author

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

@tegon tegon force-pushed the let-signup-validations branch 3 times, most recently from 3dd45e9 to fb61222 Compare March 12, 2018 20:47
Copy link
Collaborator

@feliperenan feliperenan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💪

@@ -3,6 +3,12 @@
require 'test_helper'

class TrackableHooksTest < Devise::IntegrationTest
test "sign in with HTTP should not run model validations" do
create_user
Copy link

@charlesoft charlesoft Mar 13, 2018

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? 🤔

@tegon tegon force-pushed the let-signup-validations branch from fb61222 to c24e754 Compare March 13, 2018 13:23
Copy link

@charlesoft charlesoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@tegon tegon merged commit 5664b19 into master Mar 14, 2018
@tegon tegon deleted the let-signup-validations branch March 14, 2018 18:21
@tegon tegon mentioned this pull request Mar 15, 2018
Hamms added a commit to code-dot-org/code-dot-org that referenced this pull request Sep 23, 2020
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.
@carlosantoniodasilva
Copy link
Member

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 save before any of that:

resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
.

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 set_user (or sign_up), I'm not sure I can see a path for us to save a record not being persisted here, thus I'm not sure we need the new_record? check in trackable.

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.

@tegon
Copy link
Member Author

tegon commented Apr 5, 2021

@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.

@carlosantoniodasilva
Copy link
Member

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

5 participants