From 5cebef64ed9f3c164db964828a62c7e0a3bde290 Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 3 May 2017 16:08:43 +0530 Subject: Rename email_verification.py to send_emails.py --- yaksh/email_verification.py | 68 --------------------------------------------- yaksh/send_emails.py | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 68 deletions(-) delete mode 100644 yaksh/email_verification.py create mode 100644 yaksh/send_emails.py diff --git a/yaksh/email_verification.py b/yaksh/email_verification.py deleted file mode 100644 index 721fa61..0000000 --- a/yaksh/email_verification.py +++ /dev/null @@ -1,68 +0,0 @@ -# Local imports -try: - from string import letters -except ImportError: - from string import ascii_letters as letters -from string import digits, punctuation -import hashlib -from random import randint -from textwrap import dedent -import smtplib - -# Django imports -from django.utils.crypto import get_random_string -from django.conf import settings - -def generate_activation_key(username): - """ Generate hashed secret key for email activation """ - chars = letters + digits + punctuation - secret_key = get_random_string(randint(10, 40), chars) - return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest() - -def send_user_mail(user_mail, key): - """ Send mail to user whose email is to be verified - This function should get two args i.e user_email and secret_key. - The activation url is generated from settings.PRODUCTION_URL and key. - """ - try: - to = user_mail - subject = 'Yaksh Email Verification' - message = dedent("""\ - To activate your account and verify your email address, - please click the following link: - {0}/exam/activate/{1} - If clicking the link above does not work, - copy and paste the URL in a new browser window instead. - For any issue, please write us on {2} - - Regards - Yaksh Team - """.format(settings.PRODUCTION_URL, key, settings.REPLY_EMAIL) - ) - - user = settings.EMAIL_HOST_USER - pwd = settings.EMAIL_HOST_PASSWORD - smtpserver = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT) - smtpserver.ehlo() - smtpserver.starttls() - smtpserver.ehlo() - smtpserver.esmtp_features['auth']='LOGIN DIGEST-MD5 PLAIN' - - smtpserver.login(user, pwd) - header = 'To:{0}\nFrom:{1}\nSubject:{2}\n'.format(to, - settings.SENDER_EMAIL, subject) - message = '{0}\n{1}\n\n'.format(header, message) - smtpserver.sendmail(user, to, message) - smtpserver.close() - - msg = "An activation link is sent to your registered email.\ - Please activate the link within 20 minutes." - success = True - - except Exception as exc_msg: - msg = """Error: {0}. Please check your email address.\ - If email address is correct then - Please contact {1}.""".format(exc_msg, settings.REPLY_EMAIL) - success = False - - return success, msg diff --git a/yaksh/send_emails.py b/yaksh/send_emails.py new file mode 100644 index 0000000..37736d5 --- /dev/null +++ b/yaksh/send_emails.py @@ -0,0 +1,58 @@ +# Local imports +try: + from string import letters +except ImportError: + from string import ascii_letters as letters +from string import digits, punctuation +import hashlib +from random import randint +from textwrap import dedent +import smtplib + +# Django imports +from django.utils.crypto import get_random_string +from django.conf import settings +from django.core.mail import send_mass_mail, send_mail + + +def generate_activation_key(username): + """ Generate hashed secret key for email activation """ + chars = letters + digits + punctuation + secret_key = get_random_string(randint(10, 40), chars) + return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest() + + +def send_user_mail(user_mail, key): + """ Send mail to user whose email is to be verified + This function should get two args i.e user_email and secret_key. + The activation url is generated from settings.PRODUCTION_URL and key. + """ + try: + to = user_mail + subject = 'Yaksh Email Verification' + message = dedent("""\ + To activate your account and verify your email address, + please click the following link: + {0}/exam/activate/{1} + If clicking the link above does not work, + copy and paste the URL in a new browser window instead. + For any issue, please write us on {2} + + Regards + Yaksh Team + """.format(settings.PRODUCTION_URL, key, settings.REPLY_EMAIL) + ) + + send_mail(subject, message, settings.SENDER_EMAIL, [to]) + + msg = "An activation link is sent to your registered email.\ + Please activate the link within 20 minutes." + success = True + + except Exception as exc_msg: + msg = """Error: {0}. Please check your email address.\ + If email address is correct then + Please contact {1}.""".format(exc_msg, settings.REPLY_EMAIL) + success = False + + return success, msg -- cgit From a76be3052276945d688ab8746679f25cd40cad3f Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 3 May 2017 16:10:28 +0530 Subject: Change forms.py, views.py and urls.py - Store activation key expiry time according to timezone for email verification and activation - Add reverse resolution in urls.py --- yaksh/forms.py | 10 ++++------ yaksh/urls.py | 6 +++--- yaksh/views.py | 9 ++++----- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/yaksh/forms.py b/yaksh/forms.py index 5dd56a2..3459be9 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -10,6 +10,7 @@ from taggit.managers import TaggableManager from taggit.forms import TagField from django.forms.models import inlineformset_factory from django.db.models import Q +from django.utils import timezone from textwrap import dedent try: from string import letters @@ -18,7 +19,7 @@ except ImportError: from string import punctuation, digits import datetime import pytz -from .email_verification import generate_activation_key +from .send_emails import generate_activation_key languages = ( ("select", "Select Language"), @@ -146,11 +147,8 @@ class UserRegisterForm(forms.Form): new_profile.is_email_verified = True else: new_profile.activation_key = generate_activation_key(new_user.username) - new_profile.key_expiry_time = datetime.datetime.strftime( - datetime.datetime.now() + \ - datetime.timedelta(minutes=20), - "%Y-%m-%d %H:%M:%S" - ) + new_profile.key_expiry_time = timezone.now() + \ + timezone.timedelta(minutes=20) new_profile.save() return u_name, pwd, new_user.email, new_profile.activation_key diff --git a/yaksh/urls.py b/yaksh/urls.py index b02b797..e4676d3 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -5,9 +5,9 @@ urlpatterns = [ url(r'^$', views.index), url(r'^login/$', views.user_login, name='login'), url(r'^logout/$', views.user_logout), - url(r'^update_email/$', views.update_email), - url(r'^activate/(?P.+)$', views.activate_user), - url(r'^new_activation/$', views.new_activation), + url(r'^update_email/$', views.update_email, name="update_email"), + url(r'^activate/(?P.+)$', views.activate_user, name="activate"), + url(r'^new_activation/$', views.new_activation, name='new_activation'), url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'), url(r'^quizzes/(?P\w+)/$', views.quizlist_user, name='quizlist_user'), url(r'^results/$', views.results_user), diff --git a/yaksh/views.py b/yaksh/views.py index 6a4325e..c7af5cc 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -42,7 +42,7 @@ from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\ from .settings import URL_ROOT from yaksh.models import AssignmentUpload from .file_utils import extract_files -from .email_verification import send_user_mail, generate_activation_key +from .send_emails import send_user_mail, generate_activation_key from .decorators import email_verified @@ -1540,10 +1540,8 @@ def new_activation(request, email=None): if not user.profile.is_email_verified: user.profile.activation_key = generate_activation_key(user.username) - user.profile.key_expiry_time = datetime.strftime( - datetime.now() + \ - timedelta(minutes=20), "%Y-%m-%d %H:%M:%S" - ) + user.profile.key_expiry_time = timezone.now() + \ + timezone.timedelta(minutes=20) user.profile.save() success, msg = send_user_mail(user.email, user.profile.activation_key) if success: @@ -1599,6 +1597,7 @@ def download_assignment_file(request, quiz_id, question_id=None, user_id=None): return response @login_required +@email_verified def duplicate_course(request, course_id): user = request.user course = get_object_or_404(Course, pk=course_id) -- cgit From 7e8f9ce4e2a04d813f022cf3677dfcd78a7f94c5 Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 3 May 2017 16:12:09 +0530 Subject: Add views tests for email verification and activation --- yaksh/test_views.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 3fcdde2..37e5ce4 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -7,6 +7,7 @@ from django.core.urlresolvers import reverse from django.test import TestCase from django.test import Client from django.utils import timezone +from django.core import mail from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ @@ -97,6 +98,31 @@ class TestProfile(TestCase): self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/view_profile.html') + def test_email_verification_for_user_post(self): + """ + POST request to verify email + """ + self.client.login( + username=self.user2.username, + password=self.user2_plaintext_pass + ) + post_response = self.client.post(reverse('yaksh:new_activation'), + data={'email':self.user2.email} + ) + subject = mail.outbox[0].subject.replace(" ", "_") + activation_key = mail.outbox[0].body.split("\n")[2].split("/")[-1] + get_response = self.client.get(reverse('yaksh:activate', + kwargs={'key': activation_key}), + follow=True + ) + updated_profile_user = User.objects.get(id=self.user2.id) + updated_profile = Profile.objects.get(user=updated_profile_user) + self.assertEqual(post_response.status_code, 200) + self.assertEqual(subject, "Yaksh_Email_Verification") + self.assertEqual(get_response.status_code, 200) + self.assertEqual(updated_profile.is_email_verified, True) + self.assertTemplateUsed(get_response, 'yaksh/activation_status.html') + def test_edit_profile_post(self): """ POST request to edit_profile view should update the user's profile @@ -141,6 +167,25 @@ class TestProfile(TestCase): self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/editprofile.html') + def test_update_email_for_user_post(self): + """ POST request to update email if multiple users with same email are + found + """ + self.client.login( + username=self.user2.username, + password=self.user2_plaintext_pass + ) + response = self.client.post(reverse('yaksh:update_email'), + data={ + 'username': self.user2.username, + 'email':"demo_user2@mail.com" + } + ) + updated_user = User.objects.get(id=self.user2.id) + self.assertEqual(updated_user.email, "demo_user2@mail.com") + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'yaksh/activation_status.html') + class TestAddQuiz(TestCase): def setUp(self): -- cgit From d7e6430d8c48381c6660906ab3a96251824f9031 Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 4 May 2017 16:37:03 +0530 Subject: Set EMAIL_BACKEND to dummy email backend for development --- online_test/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/online_test/settings.py b/online_test/settings.py index 8d0613b..26449d4 100644 --- a/online_test/settings.py +++ b/online_test/settings.py @@ -113,6 +113,10 @@ EMAIL_HOST_USER = 'email_host_user' EMAIL_HOST_PASSWORD = 'email_host_password' + +# Set EMAIL_BACKEND to 'django.core.mail.backends.smtp.EmailBackend' in production +EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' + # SENDER_EMAIL, REPLY_EMAIL, PRODUCTION_URL, IS_DEVELOPMENT are used in email # verification. Set the variables accordingly to avoid errors in production -- cgit