From 0d91e42aeefefcf3ceadc21afd338e45cba2023e Mon Sep 17 00:00:00 2001
From: Lasse Fredheim <lass@uio.no>
Date: Tue, 14 Mar 2023 12:59:08 +0100
Subject: [PATCH] Add check for if email exists in validation

---
 frontend/public/locales/en/common.json        |  1 +
 frontend/public/locales/nb/common.json        |  1 +
 frontend/public/locales/nn/common.json        |  1 +
 .../sponsor/register/stepPersonForm.tsx       |  9 -------
 frontend/src/utils/index.test.ts              | 24 ++++++++++++++-----
 frontend/src/utils/index.ts                   | 13 ++++++++--
 gregui/api/serializers/email.py               |  0
 gregui/api/urls.py                            |  7 +++++-
 gregui/api/views/email.py                     |  0
 gregui/api/views/identity.py                  | 16 +++++++++++--
 gregui/tests/api/views/test_identity.py       | 13 ++++++++++
 11 files changed, 65 insertions(+), 20 deletions(-)
 delete mode 100644 gregui/api/serializers/email.py
 delete mode 100644 gregui/api/views/email.py

diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json
index 40684e8e..1e0c48cd 100644
--- a/frontend/public/locales/en/common.json
+++ b/frontend/public/locales/en/common.json
@@ -133,6 +133,7 @@
     "roleEndRequired": "Role end date is required",
     "invalidEndDate": "Chosen role must have an earlier end date",
     "emailRequired": "E-mail is required",
+    "existingEmail": "This e-mail address is already in use",
     "consentRequired": "This consent is required",
     "invalidMobilePhoneNumber": "Invalid phone number",
     "invalidEmail": "Invalid e-mail address",
diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json
index 38363b4a..782c45b0 100644
--- a/frontend/public/locales/nb/common.json
+++ b/frontend/public/locales/nb/common.json
@@ -133,6 +133,7 @@
     "roleEndRequired": "Sluttdato for rolle er obligatorisk",
     "invalidEndDate": "Valgt rolle må ha en tidligere sluttdato",
     "emailRequired": "E-post er obligatorisk",
+    "existingEmail": "E-postadressen er allerede i bruk",
     "consentRequired": "Dette samtykket er obligatorisk",
     "invalidMobilePhoneNumber": "Ugyldig telefonnummer",
     "invalidEmail": "Ugyldig e-postadresse",
diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json
index e1669b95..fa51387b 100644
--- a/frontend/public/locales/nn/common.json
+++ b/frontend/public/locales/nn/common.json
@@ -133,6 +133,7 @@
     "roleEndRequired": "Sluttdato for rolle er påkrevd",
     "invalidEndDate": "Vald rolle må ha ein tidlegare sluttdato",
     "emailRequired": "E-post er påkrevd",
+    "existingEmail": "E-postadressa er allereie i bruk",
     "consentRequired": "Dette samtykket er påkrevd",
     "invalidMobilePhoneNumber": "Ugyldig telefonnummer",
     "invalidEmail": "Ugyldig e-postadresse",
diff --git a/frontend/src/routes/sponsor/register/stepPersonForm.tsx b/frontend/src/routes/sponsor/register/stepPersonForm.tsx
index a07b9510..e9c8f5a1 100644
--- a/frontend/src/routes/sponsor/register/stepPersonForm.tsx
+++ b/frontend/src/routes/sponsor/register/stepPersonForm.tsx
@@ -131,15 +131,6 @@ const StepPersonForm = forwardRef(
 
     useImperativeHandle(ref, () => ({ doSubmit }))
 
-    // const checkDuplicateEmail = (email: String) => {
-    //   fetch(url til api).then((res) => {
-    //     if (res.toString() === email) {
-    //       return true
-    //     }
-    //     return false
-    //   })
-    // }
-
     const validateStartDateBeforeEndDate = (startDate: Date | undefined) => {
       if (!startDate) {
         return t('validation.startDateMustBeSet')
diff --git a/frontend/src/utils/index.test.ts b/frontend/src/utils/index.test.ts
index 0c6a66c6..5067541a 100644
--- a/frontend/src/utils/index.test.ts
+++ b/frontend/src/utils/index.test.ts
@@ -30,16 +30,28 @@ test('Valid phone number', async () => {
 })
 
 test('Valid e-mail', async () => {
-  expect(isValidEmail('test@example.org')).toEqual(true)
-  expect(isValidEmail('Test.Tester@example.org')).toEqual(true)
+  isValidEmail('test@example.org').then((data) => {
+    expect(data).toEqual(true)
+  })
+  isValidEmail('test.testerson@example.org').then((data) => {
+    expect(data).toEqual(true)
+  })
 })
 
 test('Invalid e-mail', async () => {
-  expect(isValidEmail('testexample.org')).not.toEqual(true)
-  expect(isValidEmail('test')).not.toEqual(true)
+  isValidEmail('testexample.org').then((data) => {
+    expect(data).toEqual('common:validation.invalidEmail')
+  })
+  isValidEmail('test').then((data) => {
+    expect(data).toEqual('common:validation.invalidEmail')
+  })
   // Treat special characters as invalid, some services allow them though
-  expect(isValidEmail('Øyvind.Åsen@example.org')).not.toEqual(true)
-  expect(isValidEmail('Test.Tester@Ã¥sen.org')).not.toEqual(true)
+  isValidEmail('Øyvind.Åsen@example.org').then((data) => {
+    expect(data).toEqual('common:validation.invalidEmail')
+  })
+  isValidEmail('Test.Tester@Ã¥sen.org').then((data) => {
+    expect(data).toEqual('common:validation.invalidEmail')
+  })
 })
 
 test('Body has values', async () => {
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index 681f6757..1ca62996 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -102,12 +102,21 @@ export function isValidMobilePhoneNumber(
   return i18n.t<string>('common:validation.invalidMobilePhoneNumber')
 }
 
-export function isValidEmail(data: string | undefined): boolean | string {
+export async function isValidEmail(data: string | undefined) {
   if (!data) {
     return i18n.t<string>('common:validation.emailRequired')
   }
   if (validEmailRegex.test(data)) {
-    return true
+    const message = await fetch(
+      `/api/ui/v1/email/${data}`,
+      fetchJsonOpts()
+    ).then((res) => {
+      if (res.ok) {
+        return i18n.t<string>('common:validation.existingEmail')
+      }
+      return true
+    })
+    return message
   }
   return i18n.t<string>('common:validation.invalidEmail')
 }
diff --git a/gregui/api/serializers/email.py b/gregui/api/serializers/email.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/gregui/api/urls.py b/gregui/api/urls.py
index cf2dd76c..122e11e8 100644
--- a/gregui/api/urls.py
+++ b/gregui/api/urls.py
@@ -1,6 +1,10 @@
 from django.urls import re_path, path
 from rest_framework.routers import DefaultRouter
-from gregui.api.views.identity import IdentityCheckView, IdentityViewSet
+from gregui.api.views.identity import (
+    IdentityCheckView,
+    IdentityViewSet,
+    EmailExistsView,
+)
 
 from gregui.api.views.invitation import (
     CheckInvitationView,
@@ -43,4 +47,5 @@ urlpatterns += [
     ),
     path("userinfo/", UserInfoView.as_view(), name="userinfo"),
     path("identitycheck/<int:id>", IdentityCheckView.as_view(), name="identitycheck"),
+    path("email/<str:value>", EmailExistsView.as_view(), name="email"),
 ]
diff --git a/gregui/api/views/email.py b/gregui/api/views/email.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/gregui/api/views/identity.py b/gregui/api/views/identity.py
index e9593e82..d511db28 100644
--- a/gregui/api/views/identity.py
+++ b/gregui/api/views/identity.py
@@ -2,10 +2,10 @@ from django.conf import settings
 from rest_framework.authentication import SessionAuthentication, BasicAuthentication
 from rest_framework.permissions import IsAuthenticated
 from rest_framework.viewsets import GenericViewSet
-from rest_framework import mixins
+from rest_framework import mixins, status
 from rest_framework.views import APIView
 from rest_framework.response import Response
-from rest_framework import status
+from rest_framework.generics import RetrieveAPIView
 
 from greg.models import Identity
 from greg.permissions import IsSponsor
@@ -52,3 +52,15 @@ class IdentityCheckView(APIView):
             )
         match = client.extid_search(ident.type, ident.value)
         return Response({"match": match.dict() if match else None})
+
+
+class EmailExistsView(RetrieveAPIView):
+    """
+    View used for checking if an email is already in use
+    """
+
+    authentication_classes = [SessionAuthentication, BasicAuthentication]
+    permission_classes = [IsAuthenticated, IsSponsor]
+    serializer_class = IdentitySerializer
+    queryset = Identity.objects.filter(type="private_email")
+    lookup_field = "value"
diff --git a/gregui/tests/api/views/test_identity.py b/gregui/tests/api/views/test_identity.py
index f7a1ac31..3c59514d 100644
--- a/gregui/tests/api/views/test_identity.py
+++ b/gregui/tests/api/views/test_identity.py
@@ -87,3 +87,16 @@ def test_identity_check_existing_fnr(
     url = reverse("gregui-v1:identitycheck", kwargs={"id": person_foo.fnr.id})
     response = client.get(url)
     assert response.json() == {"match": {"first": "Ola", "last": "Nordmann"}}
+
+
+@pytest.mark.django_db
+def test_existing_email(client, log_in, user_sponsor, person_foo):
+    log_in(user_sponsor)
+    response = client.get(
+        reverse("gregui-v1:email", kwargs={"value": person_foo.private_email.value})
+    )
+    assert response.status_code == 200
+    response = client.get(
+        reverse("gregui-v1:email", kwargs={"value": "nosuch@email.com"})
+    )
+    assert response.status_code == 404
-- 
GitLab