Add validation email before creation
This commit is contained in:
parent
c4079a1c1d
commit
d07ff15f69
3 changed files with 151 additions and 53 deletions
29
dal/templates/confirm_email.html
Normal file
29
dal/templates/confirm_email.html
Normal 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 %}
|
|
@ -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"),
|
||||||
]
|
]
|
155
dal/views.py
155
dal/views.py
|
@ -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,21 +133,16 @@ class Register(View):
|
||||||
pwd = r'%s' % password1
|
pwd = r'%s' % password1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ldap_manager = LdapManager()
|
|
||||||
ldap_manager.create_user(
|
|
||||||
username, pwd, firstname, lastname, email
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
return render(request, 'error.html', { 'urlname': urlname,
|
|
||||||
'service': service,
|
|
||||||
'error': e } )
|
|
||||||
# Finally, we send the send user credentials via email
|
|
||||||
creationtime = int(datetime.utcnow().timestamp())
|
creationtime = int(datetime.utcnow().timestamp())
|
||||||
# Construct the data for the email
|
base_url = "{0}://{1}".format(self.request.scheme,
|
||||||
|
self.request.get_host())
|
||||||
|
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
|
||||||
email_from = settings.EMAIL_FROM_ADDRESS
|
email_from = settings.EMAIL_FROM_ADDRESS
|
||||||
to = ['%s <%s>' % (username, email)]
|
to = ['%s <%s>' % (username, email)]
|
||||||
subject = '{}, Welcome to datacenterlight'.format(firstname)
|
subject = 'Activate your ungleich account'.format(firstname)
|
||||||
body = 'The username {} was successfully created.\n'.format(username)
|
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
|
# Build the email
|
||||||
mail = EmailMessage(
|
mail = EmailMessage(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
|
@ -114,12 +150,13 @@ class Register(View):
|
||||||
from_email=email_from,
|
from_email=email_from,
|
||||||
to=to
|
to=to
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
mail.send()
|
mail.send()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
return render(request, 'error.html', { 'urlname': urlname,
|
||||||
pass
|
'service': service,
|
||||||
return render(request, 'usercreated.html', { 'user': username } )
|
'error': e } )
|
||||||
|
return render(request, 'confirm_email.html')
|
||||||
|
|
||||||
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:
|
||||||
|
@ -338,8 +375,8 @@ class ResetRequest(View):
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue