-
Notifications
You must be signed in to change notification settings - Fork 1
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
Adding password reset email change fix #400
Conversation
/deploy arroya |
Deploying to dev cluster with following parameters:
|
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.
This needs a 2nd look. We should also try not to update the Pipfile.lock if not needed, just to limit the amount of things changing outside the scope of this PR. We also should be looking at line coverage when adding code, in this instance that would have helped identify some problems
@@ -746,6 +746,15 @@ def put_email_verification(token, tran, session): | |||
# We set the user as verified on the OAuth2 server. | |||
set_user_verified(email_address) | |||
|
|||
if not respondent.pending_email_address and respondent.password_verification_token: |
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.
This feels in the wrong place and order. The issue materializes because the email in the token has the original email and due to the pending becoming the new email makes it makes it invalid. With that in mind it should be there and not here. Having it here puts in on the original verification path which isn't valid.
It should be done inside update_verified_email_address or alternatively after line 727
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.
This has been moved up to line 729 right after the email update
@@ -746,6 +746,15 @@ def put_email_verification(token, tran, session): | |||
# We set the user as verified on the OAuth2 server. | |||
set_user_verified(email_address) | |||
|
|||
if not respondent.pending_email_address and respondent.password_verification_token: | |||
if not respondent.password_verification_token: |
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 this reachable code? As the above means respondent.password_verification_token has to be True
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 second if statement has been removed
raise NotFound("Verification token not found") | ||
|
||
# Reset password token fields in the database since they are linked to the old email | ||
delete_respondent_password_verification_token(respondent.party_uuid, session) |
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 do wonder if these 2 are intrinsically linked and should have a parent function, maybe worth checking other usages in the code
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.
They are linked but there's no parent function and adding it would require work to frontstage as well and I think is out of the scope of the card
test/party_client.py
Outdated
@@ -434,3 +434,31 @@ def get_pending_surveys_with_originator_party_id(self, party_id, expected_status | |||
def delete_pending_surveys_with_batch_no(self, batch_no, expected_status=202): | |||
response = self.client.delete(f"/party-api/v1/pending-surveys/{batch_no}", headers=self.auth_headers) | |||
self.assertStatus(response, expected_status) | |||
|
|||
def get_password_verification_token(self, party_id, expected_status=200): |
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 this used? I can't see where
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 had added these for completeness but they are now removed
test/party_client.py
Outdated
self.assertStatus(response, expected_status) | ||
return response.json | ||
|
||
def post_password_verification_token(self, party_id, payload, expected_status=200): |
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.
Same again, perhaps I am missing it
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.
as above
test/test_respondent_controller.py
Outdated
@@ -1215,6 +1215,30 @@ def test_put_email_verification_uses_case_insensitive_email_query(self): | |||
account_controller.put_email_verification(token) | |||
query.assert_called_once_with("[email protected]", db.session()) | |||
|
|||
def test_email_verification_resets_password_reset(self): | |||
self.populate_with_respondent() |
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.
This returns a respondent, so you don't need the line below
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.
It looks like it might even do a token as well
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.
This has been updated
test/test_respondent_controller.py
Outdated
db_respondent = respondents()[0] | ||
token = self.generate_valid_token_from_email(db_respondent.email_address) | ||
response = self.put_email_verification(token, 200) | ||
db_respondent = respondents()[0] |
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.
Not sure why this has been re-assigned to the same thing as above
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.
This updates the respondent variable which would otherwise fail the tests as it still contains the verification token and counter is 1
test/test_respondent_controller.py
Outdated
db_respondent = respondents()[0] | ||
self.assertIsNone(db_respondent.password_verification_token) | ||
self.assertEqual(db_respondent.password_reset_counter, 0) | ||
self.assertEqual(response["status"], RespondentStatus.ACTIVE.name) |
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.
Checking status, is this needed, there should be another test that does that?
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.
This has been removed
test/test_respondent_controller.py
Outdated
self.assertEqual(db_respondent.password_reset_counter, 0) | ||
self.assertEqual(response["status"], RespondentStatus.ACTIVE.name) | ||
|
||
def test_email_verification_no_password_reset_token(self): |
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.
Similar to the above with comments
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.
As above
test/test_respondent_controller.py
Outdated
self.get_respondent_by_id(respondent.party_uuid) | ||
with self.assertRaises(Exception): | ||
self.get_respondent_by_email(respondent_1.email_address) | ||
# def test_batch_delete_user_data_marked_for_deletion(self): |
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.
Why has this been commented out? There should be reason in the code, on the PR. Or if not needed just removed
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.
This was just to test locally since it was hanging for a long time locally, has been reverted
@@ -726,6 +726,10 @@ def put_email_verification(token, tran, session): | |||
|
|||
if respondent: | |||
update_verified_email_address(respondent, tran) | |||
if not respondent.pending_email_address and respondent.password_verification_token: |
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.
This still needs some work, to get to this line we know this is a pending email update. The respondent has received an email which when they clicks updates the email with the pending and then removes the pending_email (line 798) (i.e update_verified_email_address)
Now because of that and the order this has been put in if not respondent.pending_email_address
becomes redundant as the code underneath will always set respondent.pending_email_address to None, all we need to do is check the verification token existence
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.
This has been updated
test/party_client.py
Outdated
response = self.client.delete( | ||
f"/party-api/v1/respondents/{party_id}/password-verification-token/{token}", headers=self.auth_headers | ||
) | ||
self.assertStatus(response, expected_status) |
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.
This assert is still a bit light and password-verification-token returns return jsonify({"message": "Successfully removed token"})
which we should use
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.
added functionality to check what message is returned by the api
test/party_client.py
Outdated
@@ -434,3 +434,15 @@ def get_pending_surveys_with_originator_party_id(self, party_id, expected_status | |||
def delete_pending_surveys_with_batch_no(self, batch_no, expected_status=202): | |||
response = self.client.delete(f"/party-api/v1/pending-surveys/{batch_no}", headers=self.auth_headers) | |||
self.assertStatus(response, expected_status) | |||
|
|||
def delete_password_verification_token(self, party_id, token, expected_status=200): |
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.
We wouldn't usually have a default var in a function when it is single use. It does make me wonder how we are testing 404 failures, but that is for another story I guess
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 the password token parts the rest is getting tested in the test_account_controller.py
test/party_client.py
Outdated
) | ||
self.assertStatus(response, expected_status) | ||
|
||
def reset_password_reset_counter(self, party_id, expected_status=200): |
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.
Both comments above are valid here as well, although return jsonify({"message": "Successfully reset counter"})
this time
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.
Added functionality to check what message is returned by the api
test/test_respondent_controller.py
Outdated
self.assertIsNone(respondent.password_verification_token) | ||
self.assertEqual(respondent.password_reset_counter, 0) | ||
|
||
def test_email_verification_no_password_reset_token(self): |
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.
Do we need this function? does it not have line coverage already?
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.
It's more to check if the section doesn't fall over when there is no verification token as the rest of the tests have a token by default
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.
Ok, it's not really needed as all you are doing is removing the token and then running the code and making sure it's not there. Not major, but I would removed
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.
Definitely looking better and party is there as far as I can see, but there is more to do on this story. If you run it into GCP and request a password change and then go in and change the email (by clicking the email etc) the token is removed as expected, however if you then complete the circuit and click the password change email you get a nasty 500 in the code, because of this https://github.com/ONSdigital/ras-frontstage/blob/main/frontstage/views/passwords/reset_password.py#L29. and that is caused as the 404 is just returning https://github.com/ONSdigital/ras-frontstage/blob/main/frontstage/controllers/party_controller.py#L151 The passwords/password-token-not-found.html is probably correct, the code has not been written correctly for the 404
What and why?
This fixes the case where a user changes email while having requested a forgot password link by deleting the password reset token in the database when the party email is changed
How to test?
forgot password
path for an accountJira
https://jira.ons.gov.uk/browse/RAS-840