User registration system. See #688

- Legacy-Id: 3195
This commit is contained in:
Emilio A. Sánchez López 2011-07-13 10:19:26 +00:00
parent 0e829f975d
commit 05929b2272
13 changed files with 378 additions and 0 deletions

View file

114
ietf/registration/forms.py Normal file
View file

@ -0,0 +1,114 @@
import datetime
import hashlib
from django import forms
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.sites.models import Site
from django.utils.translation import ugettext, ugettext_lazy as _
from ietf.utils.mail import send_mail
from redesign.person.models import Person, Email
class RegistrationForm(forms.Form):
email = forms.EmailField(label="Your email")
realm = 'IETF'
expire = 3
def save(self, *args, **kwargs):
self.send_email()
return True
def send_email(self):
domain = Site.objects.get_current().domain
subject = ugettext(u'Confirm registration at %s') % domain
from_email = settings.DEFAULT_FROM_EMAIL
to_email = self.cleaned_data['email']
today = datetime.date.today().strftime('%Y%m%d')
auth = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, today, to_email, self.realm)).hexdigest()
context = {
'domain': domain,
'today': today,
'realm': self.realm,
'auth': auth,
'to_email': to_email,
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
}
send_mail(None, to_email, from_email, subject, 'registration/register_email.txt', context)
def clean_email(self):
email = self.cleaned_data.get('email', '')
if not email:
return email
if User.objects.filter(username=email).count():
raise forms.ValidationError(_('Email already in use'))
return email
class RecoverPasswordForm(PasswordResetForm):
realm = 'IETF'
def save(self):
domain = Site.objects.get_current().domain
subject = 'Password recovery at %s' % domain
from_email = settings.DEFAULT_FROM_EMAIL
today = datetime.date.today().strftime('%Y%m%d')
for user in self.users_cache:
to_email = self.cleaned_data["email"]
recovery_hash = hashlib.md5('%s%s%s%s%s' % (settings.SECRET_KEY, today, user.username, user.password, self.realm)).hexdigest()
context = {'domain': domain,
'username': user.username,
'recovery_hash': recovery_hash,
'today': today,
'realm': self.realm,
'expire': settings.DAYS_TO_EXPIRE_RECOVER_LINK,
}
send_mail(None, to_email, from_email, subject, 'registration/password_recovery_email.txt', context)
class PasswordForm(forms.Form):
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification."))
def __init__(self, *args, **kwargs):
self.username = kwargs.pop('username')
self.update_user = kwargs.pop('update_user', False)
super(PasswordForm, self).__init__(*args, **kwargs)
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
def get_password(self):
return self.cleaned_data.get('password1')
def create_user(self):
user = User.objects.create(username=self.username,
email=self.username)
person = Person.objects.create(user=user,
name=self.username,
ascii=self.username)
Email.objects.create(person=person,
address=self.username)
return user
def get_user(self):
return User.objects.get(username=self.username)
def save(self):
if self.update_user:
user = self.get_user()
else:
user = self.create_user()
user.set_password(self.get_password())
user.save()
return user

10
ietf/registration/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('ietf.registration.views',
url(r'^$', 'register_view', name='register_view'),
url(r'^confirm/(?P<username>[\w.@+-]+)/(?P<date>[\d]+)/(?P<realm>[\w]+)/(?P<registration_hash>[a-f0-9]+)/$', 'confirm_register_view', name='confirm_register_view'),
url(r'^password_recovery/$', 'password_recovery_view', name='password_recovery_view'),
url(r'^password_recovery/confirm/(?P<username>[\w.@+-]+)/(?P<date>[\d]+)/(?P<realm>[\w]+)/(?P<recovery_hash>[a-f0-9]+)/$', 'confirm_password_recovery', name='confirm_password_recovery'),
url(r'^ajax/check_username/$', 'ajax_check_username', name='ajax_check_username'),
)

View file

@ -0,0 +1,93 @@
import datetime
import hashlib
from django.conf import settings
from django.contrib.auth.models import User
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from django.utils import simplejson
from django.utils.translation import ugettext as _
from ietf.registration.forms import (RegistrationForm, PasswordForm,
RecoverPasswordForm)
def register_view(request):
success = False
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
success = True
else:
form = RegistrationForm()
return render_to_response('registration/register.html',
{'form': form,
'success': success},
context_instance=RequestContext(request))
def confirm_register_view(request, username, date, realm, registration_hash):
valid = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, date, username, realm)).hexdigest() == registration_hash
if not valid:
raise Http404
request_date = datetime.date(int(date[:4]), int(date[4:6]), int(date[6:]))
if datetime.date.today() > (request_date + datetime.timedelta(days=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK)):
raise Http404
success = False
if request.method == 'POST':
form = PasswordForm(request.POST, username=username)
if form.is_valid():
form.save()
# TODO: Add the user in the htdigest file
success = True
else:
form = PasswordForm(username=username)
return render_to_response('registration/confirm_register.html',
{'form': form, 'email': username, 'success': success},
context_instance=RequestContext(request))
def password_recovery_view(request):
success = False
if request.method == 'POST':
form = RecoverPasswordForm(request.POST)
if form.is_valid():
form.save()
success = True
else:
form = RecoverPasswordForm()
return render_to_response('registration/password_recovery.html',
{'form': form,
'success': success},
context_instance=RequestContext(request))
def confirm_password_recovery(request, username, date, realm, recovery_hash):
user = get_object_or_404(User, username=username)
valid = hashlib.md5('%s%s%s%s%s' % (settings.SECRET_KEY, date, user.username, user.password, realm)).hexdigest() == recovery_hash
if not valid:
raise Http404
success = False
if request.method == 'POST':
form = PasswordForm(request.POST, update_user=True, username=user.username)
if form.is_valid():
user = form.save()
# TODO: Update the user in the htdigest file
success = True
else:
form = PasswordForm(username=user.username)
return render_to_response('registration/change_password.html',
{'form': form,
'success': success,
'username': user.username},
context_instance=RequestContext(request))
def ajax_check_username(request):
username = request.GET.get('username', '')
error = False
if User.objects.filter(username=username).count():
error = _('This email is already in use')
return HttpResponse(simplejson.dumps({'error': error}), mimetype='text/plain')

View file

@ -195,6 +195,10 @@ LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool <lsmt@' + IETF_DOMAI
LIAISON_ATTACH_PATH = '/a/www/ietf-datatracker/documents/LIAISON/'
LIAISON_ATTACH_URL = '/documents/LIAISON/'
# Registration configuration
DAYS_TO_EXPIRE_REGISTRATION_LINK = 3
DAYS_TO_EXPIRE_RECOVER_LINK = 3
# DB redesign
USE_DB_REDESIGN_PROXY_CLASSES = True

View file

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block morecss %}
table.register-form ul.errorlist{ list-style-type: none; color: red; padding: 0px; margin: 0px; }
table.register-form p { margin-top: 0px; }
{% endblock %}

View file

@ -0,0 +1,23 @@
{% extends "registration/base.html" %}
{% block title %}Change password{% endblock %}
{% block content %}
<div id="change_password_page">
<h1>Change password</h1>
{% if success %}
<p>Your password has been updated.</p>
<p>Now you can <a href="{% url ietfauth.views.ietf_login %}">sign in</a></p>
{% else %}
<p>Hello, you can select a new password below for your user {{ username }}.</p>
<form action="" method="POST">
<table class="register-form">
{{ form }}
</table>
<div class="submit_row">
<input type="submit" value="Change password" />
</div>
</form>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,23 @@
{% extends "registration/base.html" %}
{% block title %}Confirm registration{% endblock %}
{% block content %}
<div id="confirm_register_page">
<h1>Confirm registration</h1>
{% if success %}
<p>Your email {{ email }} has been registered using the password you have select.</p>
<p>Now you can <a href="{% url ietfauth.views.ietf_login %}">sign in</a></p>
{% else %}
<p>Hello, the registration for {{ email }} is almost complete. Please, select a password.</p>
<form action="" method="POST">
<table class="register-form">
{{ form }}
</table>
<div class="submit_row">
<input type="submit" value="Register" />
</div>
</form>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends "registration/base.html" %}
{% block title %}Password recovery{% endblock %}
{% block content %}
<div id="register_page">
<h1>Password recovery</h1>
{% if success %}
<p>Your password recovery request has been received successfully. We have sent you an email with instructions on how to change your password.</p>
<p>Thank you.</p>
{% else %}
<form action="" method="POST">
<table class="register-form">
{{ form }}
</table>
<div class="submit_row">
<input type="submit" value="Recover my password" />
</div>
</form>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,13 @@
Hello,
We have received a password recovery request at {{ domain }}. In order to change the password for the user with username {{ username }} please follow or paste and copy in your browser the follwoing link:
http://{{ domain }}{% url confirm_password_recovery username today realm recovery_hash %}
This link will expire in {{ expire }} days.
If you didn't request a password recovery you can ignore this email, your credentials have been left untouched.
Best,
Your {{ domain }} team.

View file

@ -0,0 +1,58 @@
{% extends "registration/base.html" %}
{% block title %}Register{% endblock %}
{% block scripts %}
{{ block.super }}
(function($) {
var checkUsername = function() {
var field = $(this);
var url = $("#check_user_name_url").val();
var error = field.next('.username-error');
$.ajax({
url: url,
data: {username: field.val()},
dataType: 'json',
success: function(response) {
if (response.error) {
error.text(response.error);
error.show();
} else {
error.hide();
}
}
});
}
$(document).ready(function(){
$('#id_email').after(' <span class="username-error" style="display: none;"></span>');
$('#id_email').keyup(checkUsername).blur(checkUsername);
});
})(jQuery);
{% endblock %}
{% block content %}
<div id="register_page">
<h1>Register</h1>
{% if success %}
<p>Your registration request has been received successfully. We have sent you an email with instructions on how to finish the registration process.</p>
<p>Thank you.</p>
{% else %}
<form action="" method="POST">
<p>Please enter your email addres in order to register a new account.</p>
<table class="register-form">
{{ form }}
</table>
<div class="submit_row">
<input type="hidden" id="check_user_name_url" value="{% url ajax_check_username %}" />
<input type="submit" value="Register" />
</div>
</form>
<p class="recover_password_description">
I'm already registered but I forgot my password. <a href="{% url password_recovery_view %}">Please, help me recover my password.</a>
</p>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,11 @@
Hello,
In order to complete your registration on {{ domain }}, please follow this link or copy it and paste it in your web browser:
http://{{ domain }}{% url confirm_register_view to_email today realm auth %}
This link will expire in {{ expire }} days.
Best,
Your {{ domain }} team.

View file

@ -58,6 +58,7 @@ urlpatterns = patterns('',
(r'^accounts/', include('ietf.ietfauth.urls')),
(r'^doc/', include('ietf.idrfc.urls')),
(r'^wg/', include('ietf.wginfo.urls')),
(r'^registration/', include('ietf.registration.urls')),
(r'^$', 'ietf.idrfc.views.main'),
('^admin/', include(admin.site.urls)),