Skip to content

Commit

Permalink
feat(socialaccount): Email authentication based on user.email
Browse files Browse the repository at this point in the history
  • Loading branch information
pennersr committed Oct 20, 2023
1 parent f87f42d commit 26158b2
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 17 deletions.
4 changes: 1 addition & 3 deletions allauth/account/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,7 @@ def assess_unique_email(email) -> Optional[bool]:
False -- email is already in use
None -- email is in use, but we should hide that using email verification.
"""
from .models import EmailAddress

if not EmailAddress.objects.lookup([email]).exists():
if not filter_users_by_email(email):
# All good.
return True
elif not app_settings.PREVENT_ENUMERATION:
Expand Down
3 changes: 2 additions & 1 deletion allauth/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def factory(
with_email=True,
email_verified=True,
password=None,
with_emailaddress=True,
):
if not username:
username = uuid.uuid4().hex
Expand All @@ -49,7 +50,7 @@ def factory(
user_email(user, email or "")
if commit:
user.save()
if email:
if email and with_emailaddress:
EmailAddress.objects.create(
user=user, email=email, verified=email_verified, primary=True
)
Expand Down
23 changes: 12 additions & 11 deletions allauth/socialaccount/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

import allauth.app_settings
from allauth.account.models import EmailAddress
from allauth.account.utils import get_next_redirect_url, setup_user_email
from allauth.account.utils import (
filter_users_by_email,
get_next_redirect_url,
setup_user_email,
)
from allauth.core import context
from allauth.socialaccount import signals

Expand Down Expand Up @@ -328,16 +332,13 @@ def _lookup_by_socialaccount(self):

def _lookup_by_email(self):
emails = [e.email for e in self.email_addresses if e.verified]
if not emails:
return
address = (
EmailAddress.objects.lookup(emails).order_by("-verified", "user_id").first()
)
if address:
if app_settings.EMAIL_AUTHENTICATION_AUTO_CONNECT:
self.connect(context.request, address.user)
else:
self.user = address.user
for email in emails:
users = filter_users_by_email(email, is_active=True, prefer_verified=True)
if users:
self.user = users[0]
if app_settings.EMAIL_AUTHENTICATION_AUTO_CONNECT:
self.connect(context.request, self.user)
return

def get_redirect_url(self, request):
url = self.state.get("next")
Expand Down
12 changes: 10 additions & 2 deletions allauth/socialaccount/tests/test_login.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import copy
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
Expand All @@ -13,6 +14,7 @@
from allauth.socialaccount.models import SocialAccount


@pytest.mark.parametrize("with_emailaddress", [False, True])
@pytest.mark.parametrize("auto_connect", [False, True])
@pytest.mark.parametrize("setting", ["off", "on-global", "on-provider"])
def test_email_authentication(
Expand All @@ -25,6 +27,7 @@ def test_email_authentication(
rf,
mailoutbox,
auto_connect,
with_emailaddress,
):
"""Tests that when an already existing email is given at the social signup
form, enumeration preventation kicks in.
Expand All @@ -48,7 +51,7 @@ def test_email_authentication(
settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION = False
settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = auto_connect

user = user_factory()
user = user_factory(with_emailaddress=with_emailaddress)

sociallogin = sociallogin_factory(email=user.email, provider="unittest-server")

Expand All @@ -69,7 +72,12 @@ def test_email_authentication(
assert not added_signal.called
assert not updated_signal.called
else:
assert resp["location"] == "/accounts/profile/"
if with_emailaddress:
assert resp["location"] == "/accounts/profile/"
else:
# user.email is set, but not verified.
assert resp["location"] == reverse("account_email_verification_sent")
assert get_user_model().objects.count() == 1
assert SocialAccount.objects.filter(user=user.pk).exists() == auto_connect
assert added_signal.called == auto_connect
assert not updated_signal.called

0 comments on commit 26158b2

Please sign in to comment.