-
Notifications
You must be signed in to change notification settings - Fork 2
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
fix: Two accounts with the same email #2642
fix: Two accounts with the same email #2642
Conversation
0f0a72c
to
159c110
Compare
159c110
to
7384709
Compare
authentication/pipeline/user.py
Outdated
if User.objects.filter(email__iexact=data["email"]).exists(): | ||
raise InvalidEmail(backend) |
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.
What would happen if we throw UnexpectedExistingUserException
here instead of InvalidEmail
exception, I feel like throwing InvalidEmail might be a little too general because the email is valid & passes the validation but the problem is that it already exists.
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 InvalidEmail
exception is being handled here.
mitxpro/authentication/serializers.py
Line 92 in 333ab77
except InvalidEmail: |
existing-user
state in the API on the other hand UnexpectedExistingUserException
returns the error
state in the API and there is no error message. Here are screenshots where I raised UnexpectedExistingUserException
.Backend:
Frontend:
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 was looking around and checking what might be the right exception to throw here and it looks like there is AuthAlreadyAssociated exception from social-core
which we should throw here and add a catch statement in the serializer along with InvalidEmail
.
if user: | ||
updated_user = serializer.save() | ||
return {"is_new": True, "user": updated_user, "username": updated_user.username} | ||
|
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.
In the above check where we are checking if it exists, Are we always going to get the user object in this step of the pipeline?
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 added this check in the previous PR where we allowed a user to save his information again on Step 1 of the Registration Form. But after this issue is raised, we are not allowing users to go back to step 1 and save the information again. As we talked about before starting work on this issue. So, I've removed this check, because now we don't need this check here.
authentication/serializers.py
Outdated
.strip() | ||
.lower() |
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.
If we have started to make the email lower in the pipeline step, Do we still need to again this lower function here ?
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.
If we need it here, we should add test for this 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.
As mentioned in the above comment #2642 (comment). This is where the InvalidEmail
exception is being handled. So adding .strip().lower()
is required here to check if the user exists in the InvalidEmail
Exception.
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've removed .strip().lower()
from here and fixed the query to check the user with case insensitive email
@@ -245,7 +245,7 @@ class UserSerializer(serializers.ModelSerializer): | |||
|
|||
def validate_email(self, value): | |||
"""Empty validation function, but this is required for WriteableSerializerMethodField""" | |||
return {"email": value} | |||
return {"email": value.strip().lower()} |
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 to lower the email here too?
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 added this check here if a user updates his/her email. It'll always be saved in lowercase. At the time of registration and on updating his/her account info
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've removed this .strip().lower()
check from the pipeline so now it is definitely required here
7384709
to
2c02384
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.
I was able to test the PR and it seems to be working except one case that might confuse the user and we should try to fix that if possible. I also added a comment regarding throwing the right exception.
So there is a scenario when we get a duplicate username collision, I'm adding the logs for that below.
To get into this scenario:
- Create a user with a an email address complete registration
- Create another user with appended
+1
in the email, and see the logs. The usernamify function generates the same username for this user as well and gets an error from DB. - On Master, The register form stays there and the
Continue
button is enabled again, When we click this it reattempts the user namify and this time create the right user. (If it's not reproduced with this step, try a manual intervention to create the username collision from usernamify function) - In case of this PR, When username collision happens, and user clicks the
Continue
button again it is redirected to the login page sayingUser already exists
which can confuse the user. Maybe we should try to handle this case.
The error log:
db_1 | 2023-05-15 09:54:50.317 UTC [1120] ERROR: duplicate key value violates unique constraint "users_user_username_key"
db_1 | 2023-05-15 09:54:50.317 UTC [1120] DETAIL: Key (username)=(arslan-ashraf) already exists.
db_1 | 2023-05-15 09:54:50.317 UTC [1120] STATEMENT: INSERT INTO "users_user" ("password", "last_login", "is_superuser", "created_on", "updated_on", "username", "email", "name", "is_staff", "is_active") VALUES ('<>, NULL, false, '2023-05-15T09:54:50.312660+00:00'::timestamptz, '2023-05-15T09:54:50.312681+00:00'::timestamptz, 'arslan-ashraf', 'arslan.ashraf+20@arbisoft.com', 'Arslan Ashraf20', false, false) RETURNING "users_user"."id"
authentication/pipeline/user.py
Outdated
if User.objects.filter(email__iexact=data["email"]).exists(): | ||
raise InvalidEmail(backend) |
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 was looking around and checking what might be the right exception to throw here and it looks like there is AuthAlreadyAssociated exception from social-core
which we should throw here and add a catch statement in the serializer along with InvalidEmail
.
I have followed the steps provided, and I did not encounter the mentioned issue. Based on my understanding, this issue should not occur as a result of this PR. The PR specifically checks for existing users based on their email addresses, not their usernames. |
0c530e5
to
934e5de
Compare
@aliraza-abbasi Sorry if I was unclear in my comment, I didn't mean that issue is being raised because of this PR, I'm saying that the behavior is changed in this PR when that issue raises:
|
@arslanashraf7 My apologies for the confusion in my previous response. Clicking the "Continue" button again is equivalent to returning from Step 2 to Step 1. In this particular scenario I've done some testing and found a very sewer issue before making this PR, let's discuss it over a call tomorrow. |
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.
Looks good 👍 Just a couple of minor changes, Feel free to rebase/merge after that.
934e5de
to
9956f36
Compare
Pre-Flight checklist
app.json
What are the relevant tickets?
#2633
What's this PR do?
Fixes the two account issue with one email #2633
How should this be manually tested?
Create Account
on localxpro
Create Account
again and enter the same email, but all uppercaseScreenshots (if appropriate)