1
0
mirror of https://github.com/krislamo/DJ-BaseSite synced 2025-09-11 13:39:29 +00:00

Added the deactivation and account recovery system

Added the deactivation and account recovery systems.  The variable
EMAIL_MESSAGE was replaced with ACTIVATE_EMAIL, then RECOVERY_EMAIL was
added. The response variable was changed in all views to the correct
spelling.  The validation file has a new function to validate the
regular expressions in an email string and not check the database. And
finally the function UserActivationKey in views was renamed to KeyGen
This commit is contained in:
2012-10-20 14:01:18 -07:00
parent 8c5b68fd0d
commit dce89a3acf
18 changed files with 511 additions and 93 deletions

View File

@@ -1 +1,2 @@
@python manage.py runserver
@pause

View File

@@ -1 +1,2 @@
@python manage.py syncdb
@pause

View File

@@ -4,4 +4,6 @@ from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
activated = models.BooleanField()
activatekey = models.CharField(max_length=25, blank=True)
activate_key = models.CharField(max_length=25, blank=True)
recovery_key = models.CharField(max_length=25, blank=True)
recovery_time = models.DateTimeField(blank=True)

View File

@@ -0,0 +1,5 @@
Hello <$user>,
Seems you have lost access to your account. The recovery link expires at: <$time> UTC
Recovery links last 2 hours.
Recovery link: <$recoverylink>

View File

@@ -18,6 +18,9 @@ TEMPLATE_DEBUG = DEBUG
baseurl = "<%baseurl%>" # "example.com"
base_title = "<%basetitle%>"
# Time zone support
USE_TZ = True
'''
You need to sign up at http://recaptcha.net/ for a public/private key to use their CAPTCHA service.
'''
@@ -31,7 +34,8 @@ EMAIL_HOST_USER = "<%HOSTemail%>"
EMAIL_HOST_PASSWORD = "<%HOSTpass%>"
EMAIL_PORT = 587
EMAIL_MESSAGE = ROOTDIR + "/<%myproject%>/activation_email.html"
ACTIVATE_EMAIL = ROOTDIR + "/<%myproject%>/activation_email.html"
RECOVERY_EMAIL = ROOTDIR + "/<%myproject%>/recovery_email.html"
ADMINS = (
("<%admin_name%>", "<%admin_email%>"),

View File

@@ -59,6 +59,11 @@ table.loginform td {
.returnlink a:hover {color:#000000;} /* mouse over link */
.returnlink a:active {color:#000000;} /* selected link */
.cannotlogin {
text-align: center;
}
/* REGISTER CSS */
.register_form {
text-align: center;

View File

@@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<div class="activated">
The account "{{user_name}}" has been deactivated. No one with this email can reregister unless you contact the admin.
<div class="activated_login"><a href="/login/">Login</a></div>
</div>
{% endblock %}

View File

@@ -19,7 +19,7 @@
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>Pass</td>
<td>Password</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
@@ -27,4 +27,8 @@
</tr>
</table>
</form>
{% endblock %}
<div class="cannotlogin">
<a href="/recovery/">Forgot password?</a> | <a href="/register/">Need an account?</a>
</div>
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block content %}
<h1>Account Recovery</h1>
<form action="/recovery/" method="post">
{% csrf_token %}
<table class="recovery_form" align="center">
<tr>
<td>User</td>
<td><input type="text" name="usern" /></td>
</tr>
<tr>
<td>Email</td>
<td><input type="text" name="email" /></td>
</tr>
<tr>
<td colspan="2" class="humantest">
{{captcha_test|safe}}
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Recover"></td>
</tr>
</table>
</form>
{% endblock %}

View File

@@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block content %}
<h1>Account Recovery</h1>
<form action="/recover/" method="post">
<input type="hidden" value="{{the_key|safe}}" name="key" />
<input type="hidden" value="{{the_user|safe}}" name="user" />
{% csrf_token %}
<table class="recovery_form" align="center">
<tr>
<td>Password</td>
<td><input type="password" name="p1" /></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="p2" /></td>
</tr>
<tr>
<td colspan="2" class="humantest">
{{captcha_test|safe}}
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Recover Account"></td>
</tr>
</table>
</form>
{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
<div class="recoverysuccess">
Password successfully changed. Recovery success. Next time just remember your password ;)
</div>
{% endblock %}

View File

@@ -21,11 +21,11 @@
<td><input type="text" name="usern" /></td>
</tr>
<tr>
<td>Pass<span class="asterisk">*</span></td>
<td>Password<span class="asterisk">*</span></td>
<td><input type="password" name="passw" /></td>
</tr>
<tr>
<td>Pass (again)<span class="asterisk">*</span></td>
<td>Password (again)<span class="asterisk">*</span></td>
<td><input type="password" name="repassw" /></td>
</tr>
<tr>

View File

@@ -25,7 +25,9 @@ urlpatterns = patterns('<%myproject%>.views',
url(r'^logout/$', 'logout_user'),
url(r'^register/$', 'register_user'),
url(r'^activate/$', 'activate_user'),
#url(r'^deactivate/$', 'activate_user'), Deactivate needs to be written.
url(r'^deactivate/$', 'deactivate_user'),
url(r'^recovery/$', 'recover_user'),
url(r'^recover/$', 'recover_attempt')
)
urlpatterns += staticfiles_urlpatterns()

View File

@@ -68,5 +68,10 @@ def clean_usernameRE(data):
else:
return False
def clean_emailRE(data):
if match("^[\w\d._%-+]+@[\w\d._%-]+.[\w]{2,6}$", data):
return data
else:
return False

View File

@@ -12,10 +12,11 @@ Please take a momemt to read the short 3 Clause LICENSE file.
# Built in imports
import random
import hashlib
import datetime
# Responce imports
from django.http import HttpResponseRedirect
# response imports
from django.shortcuts import render_to_response, RequestContext
from django.http import HttpResponseRedirect
# Authentication/Session/Validation imports
from django.contrib.auth import authenticate, login, logout
@@ -26,12 +27,16 @@ from django.core.exceptions import ObjectDoesNotExist
import validation as v
import captcha
# Time related Django imports
from django.utils.timezone import now
# Email imports
from django.core.mail import EmailMessage
from django.core import mail
# Variables from Settings.py
from settings import EMAIL_HOST_USER, EMAIL_MESSAGE
from settings import EMAIL_HOST_USER, ACTIVATE_EMAIL, RECOVERY_EMAIL
from settings import captcha_publickey, captcha_privatekey
from settings import baseurl, base_title
# User Profile model
@@ -55,7 +60,12 @@ def get_or_create_profile(user):
try:
profile = user.get_profile()
except ObjectDoesNotExist:
profile = UserProfile(activated=True, user=user)
profile = UserProfile(
activated=True,
recovery_time=now(),
user=user
)
profile.save()
return profile
@@ -66,8 +76,9 @@ def get_ip(request):
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def UserActivationKey():
def KeyGen():
random.seed()
choices = "abcdefghijklmnopqrstuvwxyzABCDEFG0123456789"
@@ -93,11 +104,12 @@ def index(request):
else:
user_navigation = user_nav(False)
responce = render_to_response('index.html', locals())
return responce
response = render_to_response('index.html', locals())
return response
def logout_user(request):
logout(request)
user_navigation = user_nav(False)
return render_to_response('auth/logged_out.html', locals())
def login_user(request):
@@ -136,20 +148,21 @@ def login_user(request):
# User account is activated (via email)
login(request, user)
user_name = user.username
responce = render_to_response('auth/logged_in.html', locals())
user_navigation = user_nav(user.username)
response = render_to_response('auth/logged_in.html', locals())
else:
# The account is not activated via email
error = "Please activate your account through email."
responce = render_to_response('error.html', locals())
response = render_to_response('error.html', locals())
else:
# The account is disabled. No login.
message = "Your account has been disabled."
responce = render_to_response('auth/disabled.html', locals())
response = render_to_response('auth/disabled.html', locals())
else:
# No object so the username and password are invalid.
login_errors = True
responce = render_to_response(
response = render_to_response(
'auth/login.html',
locals(),
context_instance=RequestContext(request)
@@ -157,7 +170,7 @@ def login_user(request):
else:
# User isn't online and hasn't sent any POST data, give them a login form.
responce = render_to_response(
response = render_to_response(
'auth/login.html',
locals(),
context_instance=RequestContext(request)
@@ -167,9 +180,9 @@ def login_user(request):
# User is logged on, don't let them login until he's logged out.
user_navigation = user_nav(request.user.username)
error = "You're already logged on."
responce = render_to_response('error.html',locals())
response = render_to_response('error.html',locals())
return responce
return response
def register_user(request):
global base_title
@@ -261,11 +274,18 @@ def register_user(request):
new_user.save()
# Create activation key and user profile
activation_key = UserActivationKey()
activation_key = KeyGen()
# Add 2 hours so a recovery key can be made instantly after
# account creation.
thetime = new_user.date_joined + datetime.timedelta(hours=2)
profile = UserProfile(
activatekey=activation_key,
activate_key=activation_key,
activated=False,
recovery_time=thetime,
user=new_user)
profile.save()
# User is created and saved. Send an activation link via email
@@ -278,13 +298,15 @@ def register_user(request):
message_deactivateurl = baseurl+"/deactivate/?key="+str(activation_key)
message_deactivateurl = message_deactivateurl+"&user="+str(new_user.username)
f = open(EMAIL_MESSAGE, 'r')
# Open email and replace data
f = open(ACTIVATE_EMAIL, 'r')
message = f.read()
message = message.replace("<$user>", str(new_user.username))
message = message.replace("<$activatelink>", message_activateurl)
message = message.replace("<$disablelink>", message_deactivateurl)
# Send email
email = EmailMessage(
"Account Activation",
message,
@@ -297,7 +319,7 @@ def register_user(request):
# Return new account page
accountname = new_user.username
responce = render_to_response(
response = render_to_response(
'auth/newaccount.html',
locals(),
context_instance=RequestContext(request)
@@ -305,7 +327,7 @@ def register_user(request):
else:
# Return registration form with errors in registration_errors
responce = render_to_response(
response = render_to_response(
'auth/registration.html',
locals(),
context_instance=RequestContext(request)
@@ -313,7 +335,7 @@ def register_user(request):
# If user hasn't sent POST data (not logged on)
else:
responce = render_to_response(
response = render_to_response(
'auth/registration.html',
locals(),
context_instance=RequestContext(request)
@@ -323,14 +345,14 @@ def register_user(request):
else:
user_navigation = user_nav(request.user.username)
error = "You cannot register while logged in."
responce = render_to_response(
response = render_to_response(
'error.html',
locals()
)
return responce
return response
def activate_user(request):
if request.method == 'GET':
if request.method == 'GET' and not request.user.is_authenticated():
# Check if data could be valid through regex
key = v.clean_key(request.GET["key"])
u_name = v.clean_usernameRE(request.GET["user"])
@@ -341,8 +363,16 @@ def activate_user(request):
# Check profile for key and compare.
user = User.objects.get(username=u_name)
user_profile = get_or_create_profile(user)
# You're already activated
if user_profile.activated:
key_correct = False
# You're disabled.
elif user.is_active == False:
key_correct = False
if user_profile.activatekey == key:
elif user_profile.activate_key == key:
# Activate user
user_profile.activated = True
user_profile.save()
@@ -355,17 +385,295 @@ def activate_user(request):
else:
key_correct = False
if key_correct:
user_name = user.username
responce = render_to_response(
'auth/activated.html',
locals()
)
else:
error = "Activation failed."
responce = render_to_response(
'error.html',
locals()
)
user_navigation = user_nav(False)
if key_correct:
user_name = user.username
response = render_to_response(
'auth/activated.html',
locals()
)
else:
error = "Activation failed."
response = render_to_response(
'error.html',
locals()
)
return responce
return response
# Logged on or didn't give GET data.
return HttpResponseRedirect('/')
def deactivate_user(request):
if request.method == 'GET' and not request.user.is_authenticated():
# Check if data could be valid through regex
key = v.clean_key(request.GET["key"])
u_name = v.clean_usernameRE(request.GET["user"])
# If key and username are valid
if request.GET["key"] == key and u_name:
try:
# Check profile for key and compare.
user = User.objects.get(username=u_name)
user_profile = get_or_create_profile(user)
# If you wish to have your users deactivate with the same
# link sent in activation, remove this if statement
if user_profile.activated:
key_correct = False
elif user_profile.activate_key == key:
# Disable account.
user_profile.activated = False
user_profile.save()
user.is_active = False
user.save()
key_correct = True
else:
key_correct = False
except ObjectDoesNotExist:
key_correct = False
else:
key_correct = False
if key_correct:
user_name = user.username
response = render_to_response(
'auth/deactivated.html',
locals()
)
else:
error = "Deactivation failed."
response = render_to_response(
'error.html',
locals()
)
return response
# Logged on or didn't give GET data.
return HttpResponseRedirect('/')
def recover_user(request):
global base_title
global global_nav, user_nav
title = base_title + "Recovery"
global_navigation=global_nav()
# If user is not logged on
if not request.user.is_authenticated():
# Return user navigation for an anonymous session
user_navigation = user_nav(False)
# Set up captcha html.
captcha_test = captcha.displayhtml(captcha_publickey)
# If user has sent POST data (not logged in)
if request.method == 'POST':
# Check info via regex
u_name = v.clean_usernameRE(request.POST["usern"])
email = v.clean_emailRE(request.POST["email"])
if email == request.POST["email"] and u_name:
try:
user = User.objects.get(username__iexact=u_name)
user_profile = get_or_create_profile(user)
# Current time
time_now = now()
# Recovery time
recovery_time = user_profile.recovery_time
if time_now > recovery_time:
# Key has been requested too many times in 2 hours.
error = "Recovery keys can only be requested once every 2 hours."
response = render_to_response(
'error.html',
locals()
)
else:
# Connect to SMTP server
connection = mail.get_connection()
connection.open()
# Create a recovery key
user_profile.recovery_key = KeyGen()
user_profile.save()
# Create account recovery link
message_recoveryurl = baseurl+"/recover/?key="+str(user_profile.recovery_key)
message_recoveryurl = message_recoveryurl+"&user="+str(user.username)
# Open email template
f = open(RECOVERY_EMAIL, 'r')
message = f.read()
print message
# Replace information
message = message.replace("<$user>", str(user.username))
message = message.replace("<$recoverylink>", message_recoveryurl)
message = message.replace("<$time>", str(user_profile.recovery_time))
# Send email
email = EmailMessage(
"Account Recovery",
message,
EMAIL_HOST_USER,
[user.email]
)
email.send()
connection.close()
# Tell user to check their email.
error = "Check your email for a recovery link."
response = render_to_response(
'error.html',
locals()
)
except User.DoesNotExist:
error = "No user with that email exists."
response = render_to_response(
'error.html',
locals()
)
else:
error = "No user with that email exists."
response = render_to_response(
'error.html',
locals()
)
else:
# Didn't submit, give recovery form.
response = render_to_response(
'auth/recovery.html',
locals(),
context_instance=RequestContext(request)
)
# You're signed in, no recovery for you.
else:
return HttpResponseRedirect('/')
return response
def recover_attempt(request):
global base_title
global global_nav, user_nav
title = base_title + "Recovery"
global_navigation=global_nav()
# If user is not logged on
if request.method == 'GET' and not request.user.is_authenticated():
# Check if data could be valid through regex
key = v.clean_key(request.GET["key"])
u_name = v.clean_usernameRE(request.GET["user"])
# If valid data
if request.GET["key"] == key and u_name:
# return new password form
the_user = u_name
the_key = key
response = render_to_response(
'auth/recoveryattempt.html',
locals(),
context_instance=RequestContext(request)
)
else:
error = "User does not exist."
response = render_to_response(
'error.html',
locals()
)
# If user isn't online and is sending post data
elif request.method == 'POST' and not request.user.is_authenticated():
# Check if data could be valid through regex
key = v.clean_key(request.POST["key"])
u_name = v.clean_usernameRE(request.POST["user"])
# If key/username is validated by regex
if request.POST["key"] == key and u_name:
try:
# Check profile for key and compare.
user = User.objects.get(username=u_name)
user_profile = get_or_create_profile(user)
# Get database key and key time limit
key_db = user_profile.recovery_key
keylimit_db = user_profile.recovery_time
# Current time
time_now = now()
# If the key hasn't expired and is correct
if now() < keylimit_db and key_db == key:
password = v.clean_password(request.POST["p1"])
recover_error = ""
if not request.POST["p1"] == request.POST["p2"]:
recover_error = "Passwords don't match."
elif password == None:
recover_error = "No password entered."
elif password == -1:
recover_error = "Passwords have to be at least 5 characters."
# If there is an error
if recover_error != '':
# Set error variable for template
error = recover_error
response = render_to_response(
'error.html',
locals()
)
else:
# No errors, change password
user.set_password(password)
user.save()
# Expire recovery time.
user_profile.recovery_time = now()
user_profile.save()
response = render_to_response(
'auth/recoverysuccess.html',
locals()
)
else:
error = "Invalid key and/or username."
response = render_to_response(
'error.html',
locals()
)
except User.DoesNotExist:
error = "User doesn't exist."
response = render_to_response(
'error.html',
locals()
)
else:
error = "Invalid key and/or username."
response = render_to_response(
'error.html',
locals()
)
else:
# logged on, no recovery.
return HttpResponseRedirect('/')
return response