Skip to content

Commit 583a03d

Browse files
committed
Prohibit re-registration
1 parent 05f174e commit 583a03d

File tree

5 files changed

+41
-15
lines changed

5 files changed

+41
-15
lines changed

cvat/apps/iam/serializers.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
from django.conf import settings
1414
from django.contrib.auth.models import User
1515
from django.core.exceptions import ValidationError as DjangoValidationError
16+
from django.utils.translation import gettext_lazy as _
1617
from drf_spectacular.utils import extend_schema_field
1718
from rest_framework import serializers
1819
from rest_framework.exceptions import ValidationError
1920

2021
from cvat.apps.iam.forms import ResetPasswordFormEx
21-
from cvat.apps.iam.utils import get_dummy_user
22+
from cvat.apps.iam.utils import get_dummy_or_regular_user
2223

2324

2425
class RegisterSerializerEx(RegisterSerializer):
@@ -60,8 +61,16 @@ def save(self, request):
6061
adapter = get_adapter()
6162
self.cleaned_data = self.get_cleaned_data()
6263

64+
dummy_user, regular_user = get_dummy_or_regular_user(self.cleaned_data["email"])
65+
# A regular user registered via standard sign-up or social login method;
66+
# has an unverified email address
67+
if regular_user:
68+
raise serializers.ValidationError(
69+
_("A user is already registered with this e-mail address.")
70+
)
71+
6372
# Allow to overwrite data for dummy users
64-
user = get_dummy_user(self.cleaned_data["email"]) or adapter.new_user(request)
73+
user = dummy_user or adapter.new_user(request)
6574

6675
user = adapter.save_user(request, user, self, commit=False)
6776
if "password1" in self.cleaned_data:

cvat/apps/iam/tests/test_rest_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
from cvat.apps.engine.tests.test_rest_api import create_db_users
1414
from cvat.apps.engine.tests.utils import ApiTestBase
15-
from cvat.urls import urlpatterns as original_urlpatterns
1615
from cvat.apps.iam.views import ConfirmEmailViewEx
16+
from cvat.urls import urlpatterns as original_urlpatterns
1717

1818
urlpatterns = original_urlpatterns + [
1919
re_path(

cvat/apps/iam/utils.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,34 @@ def add_opa_rules_path(path: Path) -> None:
3333
get_opa_bundle.cache_clear()
3434

3535

36-
def get_dummy_user(email):
36+
def get_dummy_or_regular_user(email: str):
37+
"""
38+
A dummy user is created by CVAT when an invitation to an organization is sent.
39+
40+
A user is considered a dummy if:
41+
- There is only one User object with the given email
42+
- The User object has an unusable password which starts with the UNUSABLE_PASSWORD_PREFIX ("!")
43+
- The User object has no linked EmailAddress object
44+
"""
3745
from allauth.account.models import EmailAddress
3846
from allauth.account.utils import filter_users_by_email
3947

4048
users = filter_users_by_email(email)
41-
if not users or len(users) > 1:
42-
return None
49+
if not users:
50+
return None, None
51+
52+
assert len(users) == 1, "More than one user has this email"
53+
4354
user = users[0]
4455
if user.has_usable_password():
45-
return None
56+
return None, user
4657
try:
4758
EmailAddress.objects.get_for_user(user, email)
4859
except EmailAddress.DoesNotExist:
49-
return user
50-
return None
60+
return user, None
61+
62+
# account was created using social login
63+
return None, user
5164

5265

5366
def clean_up_sessions() -> None:

cvat/apps/iam/views.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@
2929
from rest_framework.permissions import AllowAny
3030
from rest_framework.response import Response
3131

32+
from cvat.apps.engine.log import ServerLogManager
33+
3234
from .authentication import Signer
3335
from .utils import get_opa_bundle
3436

37+
slogger = ServerLogManager(__name__)
38+
3539

3640
@extend_schema(tags=["auth"])
3741
@extend_schema_view(
@@ -101,9 +105,9 @@ def post(self, request, *args, **kwargs):
101105
# because redirect will make a POST request and we'll get a 404 code
102106
# (although in the browser request method will be displayed like GET)
103107
return HttpResponseBadRequest("Unverified email")
104-
# TODO: check why we need to handle exceptions here
105-
except Exception: # nosec
106-
pass
108+
# FUTURE-TODO: check why we need to handle exceptions here
109+
except Exception as ex:
110+
slogger.glob.warning(str(ex), exc_info=True)
107111

108112
self.login()
109113
return self.get_response()

cvat/apps/organizations/serializers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from rest_framework import serializers
1313

1414
from cvat.apps.engine.serializers import BasicUserSerializer
15-
from cvat.apps.iam.utils import get_dummy_user
15+
from cvat.apps.iam.utils import get_dummy_or_regular_user
1616

1717
from .models import Invitation, Membership, Organization
1818

@@ -143,8 +143,8 @@ def update(self, instance, validated_data):
143143

144144
def save(self, request, **kwargs):
145145
invitation = super().save(**kwargs)
146-
dummy_user = get_dummy_user(invitation.membership.user.email)
147-
if not to_bool(settings.ORG_INVITATION_CONFIRM) and not dummy_user:
146+
_, regular_user = get_dummy_or_regular_user(invitation.membership.user.email)
147+
if not to_bool(settings.ORG_INVITATION_CONFIRM) and regular_user:
148148
invitation.accept()
149149
else:
150150
invitation.send(request)

0 commit comments

Comments
 (0)