diff --git a/README b/README deleted file mode 100644 index 01d8296..0000000 --- a/README +++ /dev/null @@ -1,49 +0,0 @@ -DJ-BaseSite was written in Python 2.7 and Django 1.4 - -Description: -DJ-BaseSite is a customizable login and register system with required email activation. While most people just use django-registration, I decided to write my own to learn more about Django. - -NOTE TO DEVS: -A deactivation system hasn't been added. The login system needs to check attempts and display a CAPTCHA after a certain amount, this isn't included either. It also doesn't have an account recovery option. I plan to implement all of these things in future updates. - -DJ-BaseSite is released under the New BSD License (The BSD 3-Clause License), refer to the LICENSE file. - - -Instructions -1. Install Python 2.7 (python.org) and Django 1.4 (djangoproject.com) -2. Open up the config.txt file and change the data under CUSTOM VARIABLES to your information. The configuration variable meanings are below. -3. Execute the SetupProject.py script and enter the project name, it will create a project based on your configuration. -4. Run the SyncDB script or "python manage.py syncdb" and create a super user. -5. Run the RunServer script or "python manage.py runserver" and check out your new website at localhost:8000 or 127.0.0.1:8000 in your browser. - -DJ-BaseSite uses Recaptcha to prevent bots from creating accounts, so you'll need to get private and public keys from the website: http://www.google.com/recaptcha - -Configuration Variable Meanings. -=================================== --baseurl -It is used to create activation and deactivation links [in views.py in register_user()] - --admin_name/email -Adds a name and email to the ADMINS tuple in settings.py -http://docs.djangoproject.com/en/dev/ref/settings/#admins - --secret_key -A string used to provide cryptographic signing. (don't use spaces due to the configurtion system) -https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -http://www.random.org/passwords/?mode=advanced - --captcha_publickey/privatekey -DJ-BaseSite uses Recaptcha to prevent bots from creating accounts, get keys at http://www.google.com/recaptcha - --HOSTsmpt -The SMPT server address with the email used to send activation emails. If your email doesn't support SMPT I highly suggest GMAIL. - --HOSTemail -The email address - --HOSTpass -The email's password -=================================== - - -NOTE: It creates a development project, DEBUG is set to True in the settings.py so it is in no condition for deployment. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f262e4e --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# DJ-BaseSite + +DJ-BaseSite is a base Django **development** website project that adds basic user interaction to the site. Features include: The Django admin site, a login and logout system, a user registration system with required activation (via email), deactivation (an option during activation) and account recovery. + +#### _This project is currently in the Alpha phase. Therefore it is suggested you thoroughly read and test when forking, etc._ + +DJ-BaseSite was written with [Python 2.7](http://www.python.org/download/releases/2.7/) and [Django 1.4](https://www.djangoproject.com/download/) on Windows 7 Home Premium 64 bit (Service Pack 1) + +## License +DJ-BaseSite is released under the New BSD License, refer to the LICENSE file in the root of the repository before continuing. + +## Change Log + +### `0.7` (Oct 20, 2012) +* Added the deactivation and account recovery systems. +* Variable `EMAIL_MESSAGE` was replaced with `ACTIVATE_EMAIL` & `RECOVERY_EMAIL` was added. +* The `response` variable was changed in all views to the correct spelling. derp. +* Function `clean_emailRE()` was added to `validation.py` +* The function `UserActivationKey()` in `views.py` was renamed to `KeyGen()` + +### [`0.5`](https://github.com/Kris619/DJ-BaseSite/zipball/80cdb11749afa9d2ecfcbb0a91f3f867f183bfc3) (Oct 13, 2012) SHA: 80cdb11749afa9d2ecfcbb0a91f3f867f183bfc3 +* login / registration system with Django's default authentication backend +* activation system (deactivation system not implemented) +* reCAPTCHA support for registration + +## Quick Start +1. Open up the `config.txt` file and change the data under `CUSTOM VARIABLES` to your information. The configuration is explained below. +2. Execute the `SetupProject.py` script and enter a project name, it will replicate the project out of `/myproject/` to `/yourproject/` with your information. +3. Run syncdb via terminal/console in the root of the project: `python manage.py syncdb` + * Windows users will need to add the path of their Python 2.7 installation (example: `C:/Python27/`) to the [path variable](http://showmedo.com/videotutorials/video?name=960000&fromSeriesID=96) +4. Run the development server: `python manage.py runserver` + +> You should be done at this point. So check out your new website at [http://localhost:8000](http://localhost:8000) or [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser. + +## Configuration + +* `baseurl` + * Used to create activation, deactivation and recovery links +* `admin_name/email` `(`[`official documentation`](https://docs.djangoproject.com/en/1.4/ref/settings/#admins)`)` + * Adds a name and email to the ADMINS tuple in settings. On an error your website will email you logged errors. +* `secret_key` `(`[`official documentation`](https://docs.djangoproject.com/en/1.4/ref/settings/#secret-key)`)` + * A secure string used to provide cryptographic signing. It is automatically added to a [default Django project](https://docs.djangoproject.com/en/dev/intro/tutorial01/#creating-a-project) in settings. +* `captcha_publickey/privatekey` + * DJ-BaseSite uses [reCAPTCHA](http://www.google.com/recaptcha/learnmore) to prevent bots from creating accounts, so you'll need to [get a private and public key from the website](http://www.google.com/recaptcha) +* `HOSTsmtp` + * The SMTP server +* `HOSTemail` + * The email address +* `HOSTpass` + * HOSTemail's password \ No newline at end of file diff --git a/config.txt b/config.txt index e85ef9a..8192614 100644 --- a/config.txt +++ b/config.txt @@ -69,6 +69,9 @@ dir templates/admin // Activation Email %here%/myproject/myproject/activation_email.html 2 +// Recovery Email +%here%/myproject/myproject/recovery_email.html 2 + // HTML/CSS dir static dir static/css @@ -80,12 +83,16 @@ dir static/css // Auth dir templates/auth %here%/myproject/myproject/templates/auth/activated.html 2 +%here%/myproject/myproject/templates/auth/deactivated.html 2 %here%/myproject/myproject/templates/auth/disabled.html 2 %here%/myproject/myproject/templates/auth/logged_in.html 2 %here%/myproject/myproject/templates/auth/logged_out.html 2 %here%/myproject/myproject/templates/auth/login.html 2 %here%/myproject/myproject/templates/auth/newaccount.html 2 %here%/myproject/myproject/templates/auth/registration.html 2 +%here%/myproject/myproject/templates/auth/recovery.html 2 +%here%/myproject/myproject/templates/auth/recoveryattempt.html 2 +%here%/myproject/myproject/templates/auth/recoverysuccess.html 2 // Backend Django app dir backends root diff --git a/myproject/RunServer.bat b/myproject/RunServer.bat index 97e4146..46b68bd 100644 --- a/myproject/RunServer.bat +++ b/myproject/RunServer.bat @@ -1 +1,2 @@ @python manage.py runserver +@pause diff --git a/myproject/SyncDB.bat b/myproject/SyncDB.bat index 374476f..c355760 100644 --- a/myproject/SyncDB.bat +++ b/myproject/SyncDB.bat @@ -1 +1,2 @@ @python manage.py syncdb +@pause diff --git a/myproject/accountprofile/models.py b/myproject/accountprofile/models.py index bf75eb7..e3d3212 100644 --- a/myproject/accountprofile/models.py +++ b/myproject/accountprofile/models.py @@ -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) \ No newline at end of file + activate_key = models.CharField(max_length=25, blank=True) + recovery_key = models.CharField(max_length=25, blank=True) + recovery_time = models.DateTimeField(blank=True) diff --git a/myproject/myproject/recovery_email.html b/myproject/myproject/recovery_email.html new file mode 100644 index 0000000..3c5cd97 --- /dev/null +++ b/myproject/myproject/recovery_email.html @@ -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> \ No newline at end of file diff --git a/myproject/myproject/settings.py b/myproject/myproject/settings.py index 336fbdc..f9f49ed 100644 --- a/myproject/myproject/settings.py +++ b/myproject/myproject/settings.py @@ -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%>"), diff --git a/myproject/myproject/static/css/default.css b/myproject/myproject/static/css/default.css index ef7d2c0..b3ceda1 100644 --- a/myproject/myproject/static/css/default.css +++ b/myproject/myproject/static/css/default.css @@ -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; diff --git a/myproject/myproject/templates/auth/deactivated.html b/myproject/myproject/templates/auth/deactivated.html new file mode 100644 index 0000000..a6be1c2 --- /dev/null +++ b/myproject/myproject/templates/auth/deactivated.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +
+ The account "{{user_name}}" has been deactivated. No one with this email can reregister unless you contact the admin. +
Login
+
+{% endblock %} \ No newline at end of file diff --git a/myproject/myproject/templates/auth/login.html b/myproject/myproject/templates/auth/login.html index 1c01188..5a64e02 100644 --- a/myproject/myproject/templates/auth/login.html +++ b/myproject/myproject/templates/auth/login.html @@ -19,7 +19,7 @@ - Pass + Password @@ -27,4 +27,8 @@ -{% endblock %} \ No newline at end of file + +
+ Forgot password? | Need an account? +
+{% endblock %} diff --git a/myproject/myproject/templates/auth/recovery.html b/myproject/myproject/templates/auth/recovery.html new file mode 100644 index 0000000..4210bbe --- /dev/null +++ b/myproject/myproject/templates/auth/recovery.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} + +{% block content %} + +

Account Recovery

+
+{% csrf_token %} + + + + + + + + + + + + + + + +
User
Email
+ {{captcha_test|safe}} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/myproject/myproject/templates/auth/recoveryattempt.html b/myproject/myproject/templates/auth/recoveryattempt.html new file mode 100644 index 0000000..23df29b --- /dev/null +++ b/myproject/myproject/templates/auth/recoveryattempt.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block content %} + +

Account Recovery

+
+ + +{% csrf_token %} + + + + + + + + + + + + + + + +
Password
Password
+ {{captcha_test|safe}} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/myproject/myproject/templates/auth/recoverysuccess.html b/myproject/myproject/templates/auth/recoverysuccess.html new file mode 100644 index 0000000..4384f91 --- /dev/null +++ b/myproject/myproject/templates/auth/recoverysuccess.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} +
+ Password successfully changed. Recovery success. Next time just remember your password ;) +
+{% endblock %} \ No newline at end of file diff --git a/myproject/myproject/templates/auth/registration.html b/myproject/myproject/templates/auth/registration.html index fb634eb..f40f2e9 100644 --- a/myproject/myproject/templates/auth/registration.html +++ b/myproject/myproject/templates/auth/registration.html @@ -21,11 +21,11 @@ - Pass* + Password* - Pass (again)* + Password (again)* diff --git a/myproject/myproject/urls.py b/myproject/myproject/urls.py index c8fb806..348b1c0 100644 --- a/myproject/myproject/urls.py +++ b/myproject/myproject/urls.py @@ -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() \ No newline at end of file diff --git a/myproject/myproject/validation.py b/myproject/myproject/validation.py index d853d26..86e84c4 100644 --- a/myproject/myproject/validation.py +++ b/myproject/myproject/validation.py @@ -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 \ No newline at end of file diff --git a/myproject/myproject/views.py b/myproject/myproject/views.py index 3196b2e..84c3e56 100644 --- a/myproject/myproject/views.py +++ b/myproject/myproject/views.py @@ -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 + \ No newline at end of file