summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/email_verification.py55
-rw-r--r--yaksh/forms.py24
-rw-r--r--yaksh/middleware/user_time_zone.py4
-rw-r--r--yaksh/migrations/0005_auto_20170407_1048.py30
-rw-r--r--yaksh/models.py3
-rw-r--r--yaksh/templates/yaksh/activation_status.html47
-rw-r--r--yaksh/urls.py2
-rw-r--r--yaksh/views.py85
8 files changed, 231 insertions, 19 deletions
diff --git a/yaksh/email_verification.py b/yaksh/email_verification.py
new file mode 100644
index 0000000..4eded1c
--- /dev/null
+++ b/yaksh/email_verification.py
@@ -0,0 +1,55 @@
+# Local imports
+try:
+ from string import letters
+except ImportError:
+ from string import ascii_letters as letters
+from string import digits, punctuation
+# Local imports
+import hashlib
+from random import randint
+from textwrap import dedent
+
+# Django imports
+from django.utils.crypto import get_random_string
+from django.core.mail import send_mail
+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"
+ msg = 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, msg, 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 your \
+ instructor/administrator.""".format(exc_msg)
+ success = False
+
+ return success, msg
diff --git a/yaksh/forms.py b/yaksh/forms.py
index f7f7a10..ac4e1b0 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -5,7 +5,7 @@ from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Cou
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
-
+from django.conf import settings
from taggit.managers import TaggableManager
from taggit.forms import TagField
from django.forms.models import inlineformset_factory
@@ -18,6 +18,7 @@ except ImportError:
from string import punctuation, digits
import datetime
import pytz
+from .email_verification import generate_activation_key
languages = (
("select", "Select Language"),
@@ -117,6 +118,12 @@ class UserRegisterForm(forms.Form):
return c_pwd
+ def clean_email(self):
+ user_email = self.cleaned_data['email']
+ if User.objects.filter(email=user_email).exists():
+ raise forms.ValidationError("This email already exists")
+ return user_email
+
def save(self):
u_name = self.cleaned_data["username"]
u_name = u_name.lower()
@@ -135,9 +142,19 @@ class UserRegisterForm(forms.Form):
new_profile.department = cleaned_data["department"]
new_profile.position = cleaned_data["position"]
new_profile.timezone = cleaned_data["timezone"]
+ if settings.IS_DEVELOPMENT:
+ new_profile.is_email_verified = True
+ new_profile.save()
+ return u_name, pwd
+ 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.save()
-
- return u_name, pwd
+ return new_user.email, new_profile.activation_key
class UserLoginForm(forms.Form):
@@ -307,4 +324,3 @@ class QuestionPaperForm(forms.ModelForm):
class Meta:
model = QuestionPaper
fields = ['shuffle_questions']
-
diff --git a/yaksh/middleware/user_time_zone.py b/yaksh/middleware/user_time_zone.py
index 0bd4831..ff9ec5c 100644
--- a/yaksh/middleware/user_time_zone.py
+++ b/yaksh/middleware/user_time_zone.py
@@ -5,10 +5,10 @@ from django.utils import timezone
class TimezoneMiddleware(object):
""" Middleware to get user's timezone and activate timezone
- if user timezone is not available default value 'UTC' is activated """
+ if user timezone is not available default value 'Asia/Kolkata' is activated """
def process_request(self, request):
user = request.user
- user_tz = 'UTC'
+ user_tz = 'Asia/Kolkata'
if hasattr(user, 'profile'):
if user.profile.timezone:
user_tz = user.profile.timezone
diff --git a/yaksh/migrations/0005_auto_20170407_1048.py b/yaksh/migrations/0005_auto_20170407_1048.py
new file mode 100644
index 0000000..de7e2c2
--- /dev/null
+++ b/yaksh/migrations/0005_auto_20170407_1048.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-04-07 10:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('yaksh', '0004_auto_20170331_0632'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='profile',
+ name='activation_key',
+ field=models.CharField(blank=True, max_length=40, null=True),
+ ),
+ migrations.AddField(
+ model_name='profile',
+ name='is_email_verified',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='profile',
+ name='key_expiry_time',
+ field=models.DateTimeField(blank=True, null=True),
+ ),
+ ]
diff --git a/yaksh/models.py b/yaksh/models.py
index 6646615..77b5ec4 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -252,6 +252,9 @@ class Profile(models.Model):
default=pytz.utc.zone,
choices=[(tz, tz) for tz in pytz.common_timezones]
)
+ is_email_verified = models.BooleanField(default=False)
+ activation_key = models.CharField(max_length=40, blank=True, null=True)
+ key_expiry_time = models.DateTimeField(blank=True, null=True)
def get_user_dir(self):
"""Return the output directory for the user."""
diff --git a/yaksh/templates/yaksh/activation_status.html b/yaksh/templates/yaksh/activation_status.html
new file mode 100644
index 0000000..4ffd50e
--- /dev/null
+++ b/yaksh/templates/yaksh/activation_status.html
@@ -0,0 +1,47 @@
+{% extends "base.html" %}
+
+{% block pagetitle %} Yaksh Account Activation {% endblock %}
+{% block title %} Yaksh Account Activation {% endblock %}
+{% block content %}
+{% if success %}
+ <center>
+ <div class="alert alert-success" role="alert">
+ <strong> {{ msg }} </strong>
+ </div>
+ <a href="{{URL_ROOT}}/exam/"> Click Here to Login </a>
+ </center>
+{% else %}
+ {% if msg %}
+ <center>
+ <div class="alert alert-warning" role="alert">
+ <strong> {{ msg }} </strong>
+ </div>
+ </center>
+ <form action="{{ URL_ROOT }}/exam/new_activation/" method="post">
+ {% csrf_token %}
+ <center>
+ Enter Email Address for verification: <input type="email" name="email" required><br><br>
+ <button class="btn" type="submit">Resend Email</button>
+ <button class="btn" type="button" name="button"
+ onClick='location.replace("{{URL_ROOT}}/exam/");'>Cancel</button>
+ </center>
+ </form>
+ {% endif %}
+{% endif %}
+<br/>
+{% if new_activation_msg %}
+ <center>
+ <div class="alert alert-info" role="alert">
+ <strong> {{ new_activation_msg }} </strong>
+ </div>
+ <a href="{{URL_ROOT}}/exam/"> Click Here to Login </a></center>
+{% endif %}
+{% if activation_msg %}
+ <center>
+ <div class="alert alert-info" role="alert">
+ <strong> {{ activation_msg }} </strong>
+ </div>
+ <a href="{{URL_ROOT}}/exam/"> Click Here to Login </a></center>
+{% endif %}
+
+{% endblock content %} \ No newline at end of file
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 8ddfe67..fae8204 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -5,6 +5,8 @@ urlpatterns = [
url(r'^$', views.index),
url(r'^login/$', views.user_login, name='login'),
url(r'^logout/$', views.user_logout),
+ url(r'^activate/(?P<key>.+)$', views.activate_user),
+ url(r'^new_activation/$', views.new_activation),
url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'),
url(r'^quizzes/(?P<enrolled>\w+)/$', views.quizlist_user, name='quizlist_user'),
url(r'^results/$', views.results_user),
diff --git a/yaksh/views.py b/yaksh/views.py
index 44f06b1..fa72008 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -1,7 +1,7 @@
import random
import string
import os
-from datetime import datetime
+from datetime import datetime, timedelta
import collections
import csv
from django.http import HttpResponse
@@ -17,11 +17,13 @@ from django.contrib.auth.models import Group
from django.forms.models import inlineformset_factory
from django.utils import timezone
from django.core.exceptions import MultipleObjectsReturned
+from django.conf import settings
import pytz
from taggit.models import Tag
from itertools import chain
import json
import six
+from textwrap import dedent
import zipfile
try:
from StringIO import StringIO as string_io
@@ -40,6 +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
@@ -78,7 +81,7 @@ def index(request):
"""
user = request.user
if user.is_authenticated():
- if user.groups.filter(name='moderator').count() > 0:
+ if is_moderator(user):
return my_redirect('/exam/manage/')
return my_redirect("/exam/quizzes/")
@@ -93,15 +96,20 @@ def user_register(request):
ci = RequestContext(request)
if user.is_authenticated():
return my_redirect("/exam/quizzes/")
-
+ context = {}
if request.method == "POST":
form = UserRegisterForm(request.POST)
if form.is_valid():
data = form.cleaned_data
- u_name, pwd = form.save()
- new_user = authenticate(username=u_name, password=pwd)
- login(request, new_user)
- return my_redirect("/exam/quizzes/")
+ if settings.IS_DEVELOPMENT:
+ u_name, pwd = form.save()
+ new_user = authenticate(username=u_name, password=pwd)
+ login(request, new_user)
+ return index(request)
+ user_email, key = form.save()
+ success, msg = send_user_mail(user_email, key)
+ context = {'activation_msg': msg}
+ return my_render_to_response('yaksh/activation_status.html', context)
else:
return my_render_to_response('yaksh/register.html', {'form': form},
context_instance=ci)
@@ -330,19 +338,29 @@ def user_login(request):
user = request.user
ci = RequestContext(request)
+ context = {}
if user.is_authenticated():
- if user.groups.filter(name='moderator').count() > 0:
- return my_redirect('/exam/manage/')
- return my_redirect("/exam/quizzes/")
+ if not settings.IS_DEVELOPMENT:
+ if not user.profile.is_email_verified:
+ context['success'] = False
+ context['msg'] = "Your account is not verified"
+ return my_render_to_response('yaksh/activation_status.html',
+ context, context_instance=ci)
+ return index(request)
if request.method == "POST":
form = UserLoginForm(request.POST)
if form.is_valid():
user = form.cleaned_data
+ if not settings.IS_DEVELOPMENT:
+ if not user.profile.is_email_verified:
+ context['success'] = False
+ context['msg'] = "Your account is not verified. \
+ Please verify your account"
+ return my_render_to_response('yaksh/activation_status.html',
+ context, context_instance=ci)
login(request, user)
- if user.groups.filter(name='moderator').count() > 0:
- return my_redirect('/exam/manage/')
- return my_redirect('/exam/login/')
+ return index(request)
else:
context = {"form": form}
return my_render_to_response('yaksh/login.html', context,
@@ -1441,6 +1459,47 @@ def download_course_csv(request, course_id):
writer.writerow(student)
return response
+def activate_user(request, key):
+ ci = RequestContext(request)
+ profile = get_object_or_404(Profile, activation_key=key)
+ context = {}
+ context['success'] = False
+ if timezone.now() > profile.key_expiry_time:
+ context['msg'] = dedent("""
+ Your activation time expired.
+ Please try again.
+ """)
+ else:
+ context['success'] = True
+ profile.is_email_verified = True
+ profile.save()
+ context['msg'] = "Your account is activated"
+ return my_render_to_response('yaksh/activation_status.html', context,
+ context_instance=ci)
+
+def new_activation(request):
+ ci = RequestContext(request)
+ context = {}
+ if request.method == "POST":
+ email = request.POST.get('email')
+ user = get_object_or_404(User, email=email)
+ 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.save()
+ success, msg = send_user_mail(user.email, user.profile.activation_key)
+ if success:
+ context['new_activation_msg'] = msg
+ else:
+ context['msg'] = msg
+ else:
+ context['new_activation_msg'] = "Your account is already verified"
+
+ return my_render_to_response('yaksh/activation_status.html', context,
+ context_instance=ci)
@login_required
def download_assignment_file(request, quiz_id, question_id=None, user_id=None):