Repository: readthedocs/readthedocs.org

Issue:
Verify all our emails
Currently email verification is optional for users. This means when they sign up, they get a verification email, but they can still sign in without it.

We should eventually get all our active users verified. It feels like the process should be the following:

* On login, check if the email address is verified, if not, we should resend their verification email, and show a message in the UI about verifying them
* After some amount of time, we should turn on email verification as a requirement, so that we are better able to be sure our users have real emails. 


Discussion:
Hi @ericholscher I would love to work on it. :)

@rajujha373 please, go ahead and submit a pull request! :)

I suppose the description should be clear enough, but if you have any question, please ask.
@humitos This issue seems interesting to me. I would like to take this issue. Can you please guide me on how to get started with it.
@dojutsu-user you need to start by setting up your environment. Check this guide: https://docs.readthedocs.io/en/latest/install.html
@humitos thanks, but the development environment is already up, can you please tell me how to check if the email is verified or not.
Actually, I am not able to find the model for this

![screenshot from 2018-10-15 14-56-52](https://user-images.githubusercontent.com/29149191/46942410-e8869b00-d08a-11e8-9d5f-db13dd232bfa.png)

EDIT: I found it.


Resolving patch:
diff --git a/readthedocs/projects/notifications.py b/readthedocs/projects/notifications.py
--- a/readthedocs/projects/notifications.py
+++ b/readthedocs/projects/notifications.py
@@ -2,9 +2,12 @@
 
 """Project notifications."""
 
+from django.urls import reverse
 from django.http import HttpRequest
+from django.utils.translation import ugettext_lazy as _
+from messages_extends.constants import ERROR_PERSISTENT
 
-from readthedocs.notifications import Notification
+from readthedocs.notifications import Notification, SiteNotification
 from readthedocs.notifications.constants import REQUIREMENT
 
 
@@ -16,6 +19,20 @@ class ResourceUsageNotification(Notification):
     level = REQUIREMENT
 
 
+class EmailConfirmNotification(SiteNotification):
+
+    failure_level = ERROR_PERSISTENT
+    failure_message = _(
+        'Your primary email address is not verified. '
+        'Please <a href="{{account_email_url}}">verify it here</a>.',
+    )
+
+    def get_context_data(self):
+        context = super(EmailConfirmNotification, self).get_context_data()
+        context.update({'account_email_url': reverse('account_email')})
+        return context
+
+
 class DeprecatedViewNotification(Notification):
 
     """Notification to alert user of a view that is going away."""
diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py
--- a/readthedocs/projects/views/private.py
+++ b/readthedocs/projects/views/private.py
@@ -60,6 +60,7 @@
 )
 from readthedocs.projects.signals import project_import
 from readthedocs.projects.views.base import ProjectAdminMixin, ProjectSpamMixin
+from readthedocs.projects.notifications import EmailConfirmNotification
 
 from ..tasks import retry_domain_verification
 
@@ -78,9 +79,27 @@ class ProjectDashboard(PrivateViewMixin, ListView):
     model = Project
     template_name = 'projects/project_dashboard.html'
 
+    def validate_primary_email(self, user):
+        """
+        Sends a persistent error notification.
+
+        Checks if the user has a primary email or if the primary email
+        is verified or not. Sends a persistent error notification if
+        either of the condition is False.
+        """
+        email_qs = user.emailaddress_set.filter(primary=True)
+        email = email_qs.first()
+        if not email or not email.verified:
+            notification = EmailConfirmNotification(user=user, success=False)
+            notification.send()
+
     def get_queryset(self):
         return Project.objects.dashboard(self.request.user)
 
+    def get(self, request, *args, **kwargs):
+        self.validate_primary_email(request.user)
+        return super(ProjectDashboard, self).get(self, request, *args, **kwargs)
+
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
 


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py
--- a/readthedocs/rtd_tests/tests/test_notifications.py
+++ b/readthedocs/rtd_tests/tests/test_notifications.py
@@ -9,6 +9,7 @@
 from django.test import TestCase
 from django.test.utils import override_settings
 from messages_extends.models import Message as PersistentMessage
+from allauth.account.models import EmailAddress
 
 from readthedocs.builds.models import Build
 from readthedocs.notifications import Notification, SiteNotification
@@ -166,6 +167,9 @@ class TestNotification(SiteNotification):
             success_level = INFO_NON_PERSISTENT
 
         user = fixture.get(User)
+        # Setting the primary and verified email address of the user
+        email = fixture.get(EmailAddress, user=user, primary=True, verified=True)
+
         n = TestNotification(user, True)
         backend = SiteBackend(request=None)
 
diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py
--- a/readthedocs/rtd_tests/tests/test_project_views.py
+++ b/readthedocs/rtd_tests/tests/test_project_views.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
+
 from datetime import timedelta
 
+from mock import patch
 from django.contrib.auth.models import User
 from django.contrib.messages import constants as message_const
 from django.http.response import HttpResponseRedirect
@@ -9,7 +11,7 @@
 from django.utils import timezone
 from django.views.generic.base import ContextMixin
 from django_dynamic_fixture import get, new
-from mock import patch
+from allauth.account.models import EmailAddress
 
 from readthedocs.builds.models import Build, Version
 from readthedocs.oauth.models import RemoteRepository
@@ -342,6 +344,10 @@ def test_import_demo_imported_duplicate(self):
         project.repo = 'file:///foobar'
         project.save()
 
+        # Setting the primary and verified email of the test user.
+        user = User.objects.get(username='eric')
+        user_email = get(EmailAddress, user=user, primary=True, verified=True)
+
         resp = self.client.get('/dashboard/import/manual/demo/')
         self.assertEqual(resp.status_code, 302)
         self.assertEqual(resp['Location'], '/dashboard/')