diff --git a/backend/authentication/factories.py b/backend/authentication/factories.py index e5388ce71..90fb83739 100644 --- a/backend/authentication/factories.py +++ b/backend/authentication/factories.py @@ -41,7 +41,7 @@ class Meta: description = factory.Faker("text", max_nb_chars=500) verified = factory.Faker("boolean") verification_method = factory.Faker("word") - verification_code = factory.Faker("uuid4") + verifictaion_code = factory.Faker("uuid4") email = factory.Faker("email") social_links = factory.List([factory.Faker("user_name") for _ in range(3)]) is_private = factory.Faker("boolean") diff --git a/backend/authentication/serializers.py b/backend/authentication/serializers.py index 17e21fc23..7f9b731f3 100644 --- a/backend/authentication/serializers.py +++ b/backend/authentication/serializers.py @@ -186,11 +186,15 @@ def validate(self, data: Dict[str, Union[str, Any]]) -> Dict[str, Union[str, Any class PasswordResetSerializer(serializers.Serializer[UserModel]): - email = serializers.EmailField() + email = serializers.EmailField(required=False) password = serializers.CharField(write_only=True) + code = serializers.UUIDField(required=False) def validate(self, data: Dict[str, Union[str, Any]]) -> UserModel: - user = UserModel.objects.filter(email=data.get("email")).first() + if data.get("code") is not None: + user = UserModel.objects.filter(verifictaion_code=data.get("code")).first() + else: + user = UserModel.objects.filter(email=data.get("email")).first() if user is None: raise serializers.ValidationError( diff --git a/backend/authentication/tests.py b/backend/authentication/tests.py index 44ae41781..83d997b44 100644 --- a/backend/authentication/tests.py +++ b/backend/authentication/tests.py @@ -21,6 +21,7 @@ from .models import UserModel from django.test import Client from uuid import UUID +import uuid @pytest.mark.django_db @@ -210,15 +211,43 @@ def test_pwreset(client: Client) -> None: Scenarios: 1. Password reset email is sent successfully + 2. Password reset with invalid email + 3. Password reset is performed successfully + 4. Password reset with invalid verification code """ # Setup - plaintext_password = "Activist@123!?" - user = UserFactory(plaintext_password=plaintext_password) + old_password = "password123!?" + new_password = "Activist@123!?" # 1. User exists and password reset is successful + user = UserFactory(plaintext_password=old_password) response = client.get( path="/v1/auth/pwreset/", data={"email": user.email}, ) assert response.status_code == 200 assert len(mail.outbox) == 1 + + # 2. Password reset with invalid email + response = client.get( + path="/v1/auth/pwreset/", data={"email": "invalid_email@example.com"} + ) + assert response.status_code == 404 + + # 3. Password reset is performed successfully + user.verifictaion_code = uuid.uuid4() + user.save() + response = client.post( + path=f"/v1/auth/pwreset/?code={user.verifictaion_code}", + data={"password": new_password}, + ) + assert response.status_code == 200 + user.refresh_from_db() + assert user.check_password(new_password) + + # 4. Password reset with invalid verification code + response = client.post( + path="/v1/auth/pwreset/invalid_code/", + data={"password": new_password}, + ) + assert response.status_code == 404 diff --git a/backend/authentication/views.py b/backend/authentication/views.py index cfe8d9424..85f1b0ca6 100644 --- a/backend/authentication/views.py +++ b/backend/authentication/views.py @@ -218,10 +218,14 @@ def get(self, request: Request) -> Response: ) def post(self, request: Request) -> Response: - serializer = PasswordResetSerializer(data=request.data) + data = { + "password": request.data.get("password"), + "code": request.query_params.get("code"), + } + serializer = PasswordResetSerializer(data=data) serializer.is_valid(raise_exception=True) - user = serializer.validated_data + user: UserModel = serializer.validated_data user.set_password(request.data.get("password")) user.save()