Add validation email before creation

This commit is contained in:
wcolmenares 2019-05-02 17:29:01 -04:00
parent c4079a1c1d
commit d07ff15f69
3 changed files with 151 additions and 53 deletions

View file

@ -0,0 +1,29 @@
{% extends "base_short.html" %}
{% load i18n staticfiles bootstrap3 %}
{% block title %}
<title> Verify your email. </title>
{% endblock %}
{% block content %}
<div class="auth-container">
<div class="auth-bg"></div>
<div class="auth-center">
<div class="auth-content">
<div class="auth-box">
<h1 class="section-heading allcaps">{% trans " Check your email " %}</h1>
<p class="text-center">{% trans "In order to complete the sign up process, please check your email and follow the activation instructions." %}</p>
<form action="{% url 'index' %}" method="get" class="form" novalidated>
<div class="text-center">
<button type="submit" class="btn choice-btn btn-block">
{% trans "Back to indexpage" %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -13,7 +13,8 @@ from .views import (
Index, Index,
LogOut, LogOut,
ResetRequest, ResetRequest,
UserCreateAPI UserCreateAPI,
ActivateAccount
) )
urlpatterns = [ urlpatterns = [
@ -26,6 +27,7 @@ urlpatterns = [
path('index/', Index.as_view(), name="index"), path('index/', Index.as_view(), name="index"),
path('logout/', LogOut.as_view(), name="logout"), path('logout/', LogOut.as_view(), name="logout"),
path('reset/<str:user>/<str:token>/', ResetRequest.as_view()), path('reset/<str:user>/<str:token>/', ResetRequest.as_view()),
path('activate/<str:user>/<str:pwd>/<str:firstname>/<str:lastname>/<str:email>/<str:token>/', ActivateAccount.as_view()),
path('reset/', ResetRequest.as_view(), name="reset"), path('reset/', ResetRequest.as_view(), name="reset"),
path('', Index.as_view(), name="login_index"), path('', Index.as_view(), name="login_index"),
] ]

View file

@ -31,6 +31,47 @@ from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
def activate_account_link(base_url, user, pwd, firstname, lastname, email, epochutc):
tokengen = PasswordResetTokenGenerator()
pseudouser = PseudoUser()
token = tokengen.make_token(pseudouser)
buser = bytes(user, 'utf-8')
bpwd = bytes(pwd, 'utf-8')
bfirstname = bytes(firstname, 'utf-8')
blasttname = bytes(lastname, 'utf-8')
bemail = bytes(email, 'utf-8')
userpart = b64encode(buser)
pwdpart = b64encode(bpwd)
fnpart = b64encode(bfirstname)
lnpart = b64encode(blasttname)
mailpart = b64encode(bemail)
# create entry into the database
newdbentry = ResetToken(user=user, token=token, creation=epochutc)
newdbentry.save()
# set up the link
link = "{base_url}/activate/{user}/{pwd}/{fn}/{ln}/{mail}/(token)/".format(
base_url=base_url, user=userpart.decode('utf-8'),
pwd=pwdpart.decode('utf-8'),
fn=fnpart.decode('utf-8'),
ln=lnpart.decode('utf-8'),
mail=mailpart.decode('utf-8'),
token=token
)
return link
def clean_db():
"""Revoves outdated tokens"""
# cutoff time is set to 24h hours
# using utcnow() to have no headache with timezones
cutoff = int(datetime.utcnow().timestamp()) - (24*60*60)
# Get all tokens older than 24 hours
oldtokens = ResetToken.objects.all().filter(creation__lt=cutoff)
for token in oldtokens:
# delete all tokens older than 24 hours
token.delete()
return True
class Index(FormView): class Index(FormView):
template_name = "landing.html" template_name = "landing.html"
form_class = LoginForm form_class = LoginForm
@ -92,34 +133,30 @@ class Register(View):
pwd = r'%s' % password1 pwd = r'%s' % password1
try: try:
ldap_manager = LdapManager() creationtime = int(datetime.utcnow().timestamp())
ldap_manager.create_user( base_url = "{0}://{1}".format(self.request.scheme,
username, pwd, firstname, lastname, email self.request.get_host())
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
email_from = settings.EMAIL_FROM_ADDRESS
to = ['%s <%s>' % (username, email)]
subject = 'Activate your ungleich account'.format(firstname)
body = 'You can activate your ungleich account account by clicking <a href="{link}">here</a>.' \
' You can also copy and paste the following link into the address bar of your browser and follow' \
' the link in order to activate your account.\n\n{link}'.format(link=link)
# Build the email
mail = EmailMessage(
subject=subject,
body=body,
from_email=email_from,
to=to
) )
mail.send()
except Exception as e: except Exception as e:
return render(request, 'error.html', { 'urlname': urlname, return render(request, 'error.html', { 'urlname': urlname,
'service': service, 'service': service,
'error': e } ) 'error': e } )
# Finally, we send the send user credentials via email return render(request, 'confirm_email.html')
creationtime = int(datetime.utcnow().timestamp())
# Construct the data for the email
email_from = settings.EMAIL_FROM_ADDRESS
to = ['%s <%s>' % (username, email)]
subject = '{}, Welcome to datacenterlight'.format(firstname)
body = 'The username {} was successfully created.\n'.format(username)
# Build the email
mail = EmailMessage(
subject=subject,
body=body,
from_email=email_from,
to=to
)
try:
mail.send()
except Exception as e:
print(e)
pass
return render(request, 'usercreated.html', { 'user': username } )
class ChangeData(LoginRequiredMixin, View): class ChangeData(LoginRequiredMixin, View):
login_url = reverse_lazy('login_index') login_url = reverse_lazy('login_index')
@ -297,7 +334,7 @@ class ResetRequest(View):
# Cleans up outdated tokens # Cleans up outdated tokens
# If we expect quite a bit of old tokens, maybe somewhere else is better, # If we expect quite a bit of old tokens, maybe somewhere else is better,
# but for now we don't really expect many unused tokens # but for now we don't really expect many unused tokens
self.clean_db() clean_db()
# If user and token are not supplied by django, it was called from somewhere else, so it's # If user and token are not supplied by django, it was called from somewhere else, so it's
# invalid # invalid
if user == None or token == None: if user == None or token == None:
@ -336,10 +373,10 @@ class ResetRequest(View):
if password1 == "" or not password1 or password2 == "" or not password2: if password1 == "" or not password1 or password2 == "" or not password2:
return render(request, 'error.html', { 'service': service, 'error': 'Please supply a password and confirm it.' } ) return render(request, 'error.html', { 'service': service, 'error': 'Please supply a password and confirm it.' } )
if password1 != password2: if password1 != password2:
return render(request, 'error.html', { 'service': service, 'error': 'The supplied passwords do not match.' } ) return render(request, 'error.html', {'service': service, 'error': 'The supplied passwords do not match.'})
if len(password1) < 8: if len(password1) < 8:
return render(request, 'error.html', { 'service': service, 'error': 'The password is too short, please use a longer one. At least 8 characters.' } ) return render(request, 'error.html', {'service': service,
# everything checks out, now change the password 'error': 'The password is too short, please use a longer one. At least 8 characters.'})
ldap_manager = LdapManager() ldap_manager = LdapManager()
result = ldap_manager.change_password( result = ldap_manager.change_password(
@ -353,17 +390,7 @@ class ResetRequest(View):
else: else:
return render(request, 'error.html', { 'service': service, 'error': result } ) return render(request, 'error.html', { 'service': service, 'error': result } )
# Cleans up outdated tokens
def clean_db(self):
# cutoff time is set to 24h hours
# using utcnow() to have no headache with timezones
cutoff = int(datetime.utcnow().timestamp()) - (24*60*60)
# Get all tokens older than 24 hours
oldtokens = ResetToken.objects.all().filter(creation__lt=cutoff)
for token in oldtokens:
# delete all tokens older than 24 hours
token.delete()
return True
# The logged in user can change the password here # The logged in user can change the password here
@ -488,6 +515,47 @@ class PseudoUser():
pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20)) pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20))
password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30)) password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30))
class ActivateAccount(View):
def get(self, request, user=None, pwd=None, firstname=None, lastname=None, email=None, token=None):
clean_db()
if token is None:
return HttpResponse('Invalid URL', status=404)
elem_list = [user, pwd, firstname, lastname, email]
clean_list = []
for value in elem_list:
try:
value_temp = bytes(value, 'utf-8')
value_decode = b64decode(value_temp)
value_clean = value_decode.decode('utf-8')
clean_list.append(value_clean)
except Exception as e:
return HttpResponse('Invalid URL', status=404)
checks_out = False
dbentries = ResetToken.objects.all().filter(user=clean_list[0])
for entry in dbentries:
if entry.token == token:
# found the token, now delete it since it's used
checks_out = True
entry.delete()
# No token was found
if not checks_out:
return HttpResponse('Invalid URL.', status=404)
# Token was found, create user
try:
ldap_manager = LdapManager()
ldap_manager.create_user(
clean_list[0], clean_list[1], clean_list[2], clean_list[3], clean_list[4]
)
#Send welcome email
except Exception as e:
return render(request, 'error.html', {'urlname': 'register',
'service': 'register an user',
'error': e})
return render(request, 'usercreated.html', { 'user': clean_list[0] } )
class UserCreateAPI(APIView): class UserCreateAPI(APIView):
def post(self, request): def post(self, request):
@ -508,25 +576,24 @@ class UserCreateAPI(APIView):
pwd = r'%s' % User.objects.make_random_password() pwd = r'%s' % User.objects.make_random_password()
try: base_url = "{0}://{1}".format(self.request.scheme,
ldap_manager = LdapManager() self.request.get_host())
ldap_manager.create_user(
username, pwd, firstname, lastname, email
)
except Exception as e:
return Response('While trying to create the user, an error was encountered: %s' % e, 400)
# send user credentials via email
creationtime = int(datetime.utcnow().timestamp()) creationtime = int(datetime.utcnow().timestamp())
link = activate_account_link(base_url, username, pwd, firstname, lastname, email. creationtime)
# Construct the data for the email # Construct the data for the email
email_from = settings.EMAIL_FROM_ADDRESS email_from = settings.EMAIL_FROM_ADDRESS
to = ['%s <%s>' % (username, email)] to = ['%s <%s>' % (username, email)]
subject = 'Your datacenterlight credentials' subject = 'Ungleich account creation.'
body = 'Your user was successfully created.\n' body = 'A request has been sent to our servers to register you as a ungleich user.\n'
body += 'In order to complete the registration process you must ' \
'click <a href="{link}">here</a> or copy & paste the following link into the address bar of ' \
'your browser.\n{link}\n'.format(link=link)
body += 'Your credentials are:\n' body += 'Your credentials are:\n'
body += 'Username: %s\n\n' % username body += 'Username: %s\n\n' % username
body += 'Password: %s\n\n' % pwd body += 'Password: %s\n\n' % pwd
body += 'We strongly recommend you to after log in change your password.\n' body += 'We strongly recommend after the activation to log in and change your password.\n'
body += 'This link will remain active for 24 hours.\n'
# Build the email # Build the email
mail = EmailMessage( mail = EmailMessage(
subject=subject, subject=subject,
@ -537,5 +604,5 @@ class UserCreateAPI(APIView):
try: try:
mail.send() mail.send()
except: except:
return Response('User was created, but failed to send the email', 201) return Response('Failed to send the email', 201)
return Response('User successfully created', 200) return Response('Email with activation link successfully sent', 200)