diff --git a/.github/workflows/respa-ci.yml b/.github/workflows/respa-ci.yml index fad13cd82..a4d10dd2e 100644 --- a/.github/workflows/respa-ci.yml +++ b/.github/workflows/respa-ci.yml @@ -28,6 +28,7 @@ jobs: # the tests for that module which breaks. Also remove the location of source code. - name: Install python packages run: | + pip install setuptools wheel pip install -r requirements.txt # Not sure why some of the strings are not translated during test. Thus, diff --git a/requirements.in b/requirements.in index 20d0a7cac..f67552fa0 100644 --- a/requirements.in +++ b/requirements.in @@ -18,7 +18,7 @@ django-ckeditor==5.9.0 django-cors-headers django-image-cropping easy_thumbnails -git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers +git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers django-allauth djangorestframework-jwt django-storages[google] diff --git a/requirements.txt b/requirements.txt index 28286b1a8..f697201d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile +# pip-compile requirements.in # --no-binary psycopg2 @@ -45,7 +45,7 @@ django-parler==1.9.2 # via -r requirements.in, django-munigeo, django-parle django-reversion==3.0.2 # via -r requirements.in django-sanitized-dump==0.2.2 # via -r requirements.in django-storages[google]==1.12.3 # via -r requirements.in -git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers # via -r requirements.in +git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers # via -r requirements.in django==2.2.11 # via -r requirements.in, django-admin-json-editor, django-allauth, django-anymail, django-filter, django-jinja, django-mptt, django-munigeo, django-reversion, django-storages, django-tamusers, drf-oidc-auth, easy-thumbnails djangorestframework-jwt==1.11.0 # via -r requirements.in djangorestframework==3.9.1 # via -r requirements.in, django-parler-rest, drf-oidc-auth diff --git a/users/api.py b/users/api.py index 45cbfdc62..d97de6551 100644 --- a/users/api.py +++ b/users/api.py @@ -1,5 +1,8 @@ +from allauth.socialaccount.models import EmailAddress from django.contrib.auth import get_user_model -from rest_framework import permissions, serializers, generics, mixins, viewsets +from rest_framework import permissions, serializers, generics, viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response from resources.models.utils import build_ical_feed_url from resources.models import Unit @@ -69,6 +72,50 @@ def get_object(self): else: obj = self.request.user return obj + + @action(detail=True, methods=["post"], url_path="set_email") + def set_email(self, request, pk=None): + user = self.get_object() + email = request.data.get("email") + requesting_user = request.user + + if user != requesting_user: + return Response( + {"detail": "Not allowed to set email for this user."}, + status=status.HTTP_403_FORBIDDEN + ) + + if not email: + return Response( + {"detail": "Email address is required."}, + status=status.HTTP_400_BAD_REQUEST + ) + + if user.email: + return Response( + {"detail": "User already has an email set."}, + status=status.HTTP_400_BAD_REQUEST + ) + + if EmailAddress.objects.filter(email=email): + return Response( + {"detail": "The email address is already in use."}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Update the existing EmailAddress + email_address = EmailAddress.objects.filter(user=user) + if email_address: + email_address.update(email=email) + + # Update the User + user.email = email + user.save() + + return Response( + {"detail": "Email set successfully."}, + status=status.HTTP_200_OK + ) permission_classes = [permissions.IsAuthenticated] queryset = get_user_model().objects.all() diff --git a/users/tests.py b/users/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/users/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/users/tests/__init__.py b/users/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/users/tests/test_api.py b/users/tests/test_api.py new file mode 100644 index 000000000..8e8cffb84 --- /dev/null +++ b/users/tests/test_api.py @@ -0,0 +1,76 @@ +from allauth.account.models import EmailAddress +from django.contrib.auth import get_user_model +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APIClient, APITestCase + +User = get_user_model() + + +class SetUserEmailTests(APITestCase): + def setUp(self): + # Create a test user + self.user = User.objects.create_user( + username="testuser", password="testpassword", email="" + ) + self.client = APIClient() + self.client.force_authenticate(user=self.user) + EmailAddress.objects.create(user=self.user, email="") + + # Create another user + self.other_user = User.objects.create_user( + username="otheruser", password="otherpassword", email="other@example.com" + ) + EmailAddress.objects.create(user=self.other_user, email="other@example.com") + + def test_set_email_success(self): + url = reverse("user-set-email", kwargs={"pk": self.user.pk}) + data = {"email": "newemail@example.com"} + + response = self.client.post(url, data, format="json") + + self.user.refresh_from_db() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"detail": "Email set successfully."}) + self.assertEqual(self.user.email, "newemail@example.com") + self.assertTrue( + EmailAddress.objects.filter( + user=self.user, email="newemail@example.com" + ).exists() + ) + + def test_set_email_already_set(self): + # Set an email for the user first + self.user.email = "existingemail@example.com" + self.user.save() + + url = reverse("user-set-email", kwargs={"pk": self.user.pk}) + data = {"email": "anotheremail@example.com"} + + response = self.client.post(url, data, format="json") + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, {"detail": "User already has an email set."}) + self.assertEqual(self.user.email, "existingemail@example.com") + + def test_set_email_not_provided(self): + url = reverse("user-set-email", kwargs={"pk": self.user.pk}) + data = {"email": ""} # No email in the data + + response = self.client.post(url, data, format="json") + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, {"detail": "Email address is required."}) + self.assertEqual(self.user.email, "") + + def test_set_email_already_in_use(self): + url = reverse("user-set-email", kwargs={"pk": self.user.pk}) + data = {"email": "other@example.com"} # Email already used by other_user + + response = self.client.post(url, data, format="json") + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data, {"detail": "The email address is already in use."} + ) + self.assertEqual(self.user.email, "")