Registration refactored: email validation done first and registration process
finished on profile completion. django-registration removed
This commit is contained in:
parent
f133797268
commit
efa4305df0
|
@ -90,6 +90,8 @@ default_settings = dict(
|
||||||
definition='Root urlconf for the /accounts endpoints'),
|
definition='Root urlconf for the /accounts endpoints'),
|
||||||
A2_REGISTRATION_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.RegistrationForm',
|
A2_REGISTRATION_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.RegistrationForm',
|
||||||
definition='Default registration form'),
|
definition='Default registration form'),
|
||||||
|
A2_REGISTRATION_COMPLETION_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.RegistrationCompletionForm',
|
||||||
|
definition='Default registration completion form'),
|
||||||
A2_REGISTRATION_SET_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.SetPasswordForm',
|
A2_REGISTRATION_SET_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.SetPasswordForm',
|
||||||
definition='Default set password form'),
|
definition='Default set password form'),
|
||||||
A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.PasswordChangeForm',
|
A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.PasswordChangeForm',
|
||||||
|
|
|
@ -19,7 +19,7 @@ from django.contrib.auth import authenticate, login
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
|
|
||||||
|
|
||||||
import registration.views
|
from authentic2.registration_backend.views import RegistrationView
|
||||||
|
|
||||||
|
|
||||||
from authentic2.constants import NONCE_FIELD_NAME
|
from authentic2.constants import NONCE_FIELD_NAME
|
||||||
|
@ -188,7 +188,7 @@ error_ssl = SslErrorView.as_view()
|
||||||
def register(request):
|
def register(request):
|
||||||
'''Registration page for SSL auth without CA'''
|
'''Registration page for SSL auth without CA'''
|
||||||
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL)
|
next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL)
|
||||||
return registration.views.register(request, success_url=next_url,
|
return RegistrationView.as_view(request, success_url=next_url,
|
||||||
form_class=functools.partial(forms.RegistrationForm,
|
form_class=functools.partial(forms.RegistrationForm,
|
||||||
request=request))
|
request=request))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
|
|
||||||
|
from authentic2.utils import get_form_class
|
||||||
|
from . import app_settings
|
||||||
|
|
||||||
|
SET_PASSWORD_FORM_CLASS = get_form_class(
|
||||||
|
app_settings.A2_REGISTRATION_SET_PASSWORD_FORM_CLASS)
|
||||||
|
CHANGE_PASSWORD_FORM_CLASS = get_form_class(
|
||||||
|
app_settings.A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS)
|
||||||
|
|
||||||
urlpatterns = patterns('authentic2.views',
|
urlpatterns = patterns('authentic2.views',
|
||||||
url(r'^logged-in/$', 'logged_in', name='logged-in'),
|
url(r'^logged-in/$', 'logged_in', name='logged-in'),
|
||||||
|
@ -7,4 +16,24 @@ urlpatterns = patterns('authentic2.views',
|
||||||
url(r'^change-email/verify/$', 'email_change_verify',
|
url(r'^change-email/verify/$', 'email_change_verify',
|
||||||
name='email-change-verify'),
|
name='email-change-verify'),
|
||||||
url(r'^$', 'profile', name='account_management'),
|
url(r'^$', 'profile', name='account_management'),
|
||||||
|
url(r'^password/change/$',
|
||||||
|
auth_views.password_change,
|
||||||
|
{'password_change_form': CHANGE_PASSWORD_FORM_CLASS},
|
||||||
|
name='auth_password_change'),
|
||||||
|
url(r'^password/change/done/$',
|
||||||
|
auth_views.password_change_done,
|
||||||
|
name='auth_password_change_done'),
|
||||||
|
url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
||||||
|
auth_views.password_reset_confirm,
|
||||||
|
{'set_password_form': SET_PASSWORD_FORM_CLASS},
|
||||||
|
name='auth_password_reset_confirm'),
|
||||||
|
url(r'^password/reset/$',
|
||||||
|
auth_views.password_reset,
|
||||||
|
name='auth_password_reset'),
|
||||||
|
url(r'^password/reset/complete/$',
|
||||||
|
auth_views.password_reset_complete,
|
||||||
|
name='auth_password_reset_complete'),
|
||||||
|
url(r'^password/reset/done/$',
|
||||||
|
auth_views.password_reset_done,
|
||||||
|
name='auth_password_reset_done'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
from django.template.loader import render_to_string
|
|
||||||
|
|
||||||
from registration.models import RegistrationProfile
|
|
||||||
|
|
||||||
def send_activation_email(self, site):
|
|
||||||
"""
|
|
||||||
Send an activation email to the user associated with this
|
|
||||||
``RegistrationProfile``.
|
|
||||||
|
|
||||||
The activation email will make use of two templates:
|
|
||||||
|
|
||||||
``registration/activation_email_subject.txt``
|
|
||||||
This template will be used for the subject line of the
|
|
||||||
email. Because it is used as the subject line of an email,
|
|
||||||
this template's output **must** be only a single line of
|
|
||||||
text; output longer than one line will be forcibly joined
|
|
||||||
into only a single line.
|
|
||||||
|
|
||||||
``registration/activation_email.txt``
|
|
||||||
This template will be used for the body of the email.
|
|
||||||
|
|
||||||
These templates will each receive the following context
|
|
||||||
variables:
|
|
||||||
|
|
||||||
``user``
|
|
||||||
The new user account
|
|
||||||
|
|
||||||
``activation_key``
|
|
||||||
The activation key for the new account.
|
|
||||||
|
|
||||||
``expiration_days``
|
|
||||||
The number of days remaining during which the account may
|
|
||||||
be activated.
|
|
||||||
|
|
||||||
``site``
|
|
||||||
An object representing the site on which the user
|
|
||||||
registered; depending on whether ``django.contrib.sites``
|
|
||||||
is installed, this may be an instance of either
|
|
||||||
``django.contrib.sites.models.Site`` (if the sites
|
|
||||||
application is installed) or
|
|
||||||
``django.contrib.sites.models.RequestSite`` (if
|
|
||||||
not). Consult the documentation for the Django sites
|
|
||||||
framework for details regarding these objects' interfaces.
|
|
||||||
|
|
||||||
"""
|
|
||||||
ctx_dict = {'activation_key': self.activation_key,
|
|
||||||
'user': self.user,
|
|
||||||
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
|
|
||||||
'site': site}
|
|
||||||
subject = render_to_string('registration/activation_email_subject.txt',
|
|
||||||
ctx_dict)
|
|
||||||
# Email subject *must not* contain newlines
|
|
||||||
subject = ''.join(subject.splitlines())
|
|
||||||
|
|
||||||
message = render_to_string('registration/activation_email.txt',
|
|
||||||
ctx_dict)
|
|
||||||
|
|
||||||
self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
|
|
||||||
RegistrationProfile.send_activation_email = send_activation_email
|
|
|
@ -1,15 +1,72 @@
|
||||||
|
from uuid import uuid
|
||||||
|
import django
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.forms import Form, CharField, PasswordInput, EmailField
|
from django.forms import Form, CharField, PasswordInput, EmailField
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.db.models import FieldDoesNotExist
|
from django.db.models import FieldDoesNotExist
|
||||||
|
|
||||||
|
from django.contrib.auth.models import BaseUserManager, Group
|
||||||
from django.contrib.auth import forms as auth_forms
|
from django.contrib.auth import forms as auth_forms
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from django.core import signing
|
||||||
|
from django import get_version
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from .. import app_settings, compat, forms, utils, validators, widgets, fields
|
from .. import app_settings, compat, forms, utils,\
|
||||||
|
validators, widgets, fields, models
|
||||||
|
|
||||||
|
User = compat.get_user_model()
|
||||||
|
|
||||||
class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
class RegistrationForm(Form):
|
||||||
|
error_css_class = 'form-field-error'
|
||||||
|
required_css_class = 'form-field-required'
|
||||||
|
|
||||||
|
email = EmailField()
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
"""
|
||||||
|
Verify if email is unique
|
||||||
|
"""
|
||||||
|
User = compat.get_user_model()
|
||||||
|
if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE and \
|
||||||
|
User.objects.filter(email__iexact=self.cleaned_data['email']).exists():
|
||||||
|
raise ValidationError(_('This email address is already in '
|
||||||
|
'use. Please supply a different email address.'))
|
||||||
|
return self.cleaned_data['email']
|
||||||
|
|
||||||
|
def save(self, request):
|
||||||
|
data = self.cleaned_data
|
||||||
|
data.update({'next_url': request.GET.get('next_url')})
|
||||||
|
registration_token = signing.dumps(data)
|
||||||
|
ctx_dict = {'registration_url': request.build_absolute_uri(
|
||||||
|
reverse('registration_activate',
|
||||||
|
kwargs={'registration_token': registration_token})),
|
||||||
|
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
|
||||||
|
'email': data['email'],
|
||||||
|
'site': request.get_host()}
|
||||||
|
ctx_dict.update(self.cleaned_data)
|
||||||
|
|
||||||
|
subject = render_to_string('registration/activation_email_subject.txt',
|
||||||
|
ctx_dict)
|
||||||
|
|
||||||
|
subject = ''.join(subject.splitlines())
|
||||||
|
message = render_to_string('registration/activation_email.txt',
|
||||||
|
ctx_dict)
|
||||||
|
if django.VERSION >= (1, 7, 0):
|
||||||
|
html_message = render_to_string('registration/activation_email.html',
|
||||||
|
ctx_dict)
|
||||||
|
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
|
||||||
|
[data['email']], fail_silently=True,
|
||||||
|
html_message=message)
|
||||||
|
else:
|
||||||
|
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
|
||||||
|
[data['email']], fail_silently=True)
|
||||||
|
|
||||||
|
class RegistrationCompletionForm(forms.UserAttributeFormMixin, Form):
|
||||||
error_css_class = 'form-field-error'
|
error_css_class = 'form-field-error'
|
||||||
required_css_class = 'form-field-required'
|
required_css_class = 'form-field-required'
|
||||||
|
|
||||||
|
@ -21,7 +78,7 @@ class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
||||||
"""
|
"""
|
||||||
Inject required fields in registration form
|
Inject required fields in registration form
|
||||||
"""
|
"""
|
||||||
super(RegistrationForm, self).__init__(*args, **kwargs)
|
super(RegistrationCompletionForm, self).__init__(*args, **kwargs)
|
||||||
User = compat.get_user_model()
|
User = compat.get_user_model()
|
||||||
insert_idx = 0
|
insert_idx = 0
|
||||||
field_names = compat.get_registration_fields()
|
field_names = compat.get_registration_fields()
|
||||||
|
@ -38,7 +95,7 @@ class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
||||||
kwargs['validators'] = model_field.validators
|
kwargs['validators'] = model_field.validators
|
||||||
field = model_field.formfield(**kwargs)
|
field = model_field.formfield(**kwargs)
|
||||||
if isinstance(field, EmailField):
|
if isinstance(field, EmailField):
|
||||||
field = fields.EmailFieldWithValidation(**kwargs)
|
continue
|
||||||
self.fields.insert(insert_idx, field_name, field)
|
self.fields.insert(insert_idx, field_name, field)
|
||||||
insert_idx += 1
|
insert_idx += 1
|
||||||
for field_name in self.fields:
|
for field_name in self.fields:
|
||||||
|
@ -64,6 +121,7 @@ class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
||||||
self.fields['username'].help_text = app_settings.A2_REGISTRATION_FORM_USERNAME_HELP_TEXT
|
self.fields['username'].help_text = app_settings.A2_REGISTRATION_FORM_USERNAME_HELP_TEXT
|
||||||
self.fields['username'].label = app_settings.A2_REGISTRATION_FORM_USERNAME_LABEL
|
self.fields['username'].label = app_settings.A2_REGISTRATION_FORM_USERNAME_LABEL
|
||||||
|
|
||||||
|
|
||||||
def clean_username(self):
|
def clean_username(self):
|
||||||
"""
|
"""
|
||||||
Validate that the username is alphanumeric and is not already
|
Validate that the username is alphanumeric and is not already
|
||||||
|
@ -82,17 +140,6 @@ class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
||||||
else:
|
else:
|
||||||
return self.cleaned_data['username']
|
return self.cleaned_data['username']
|
||||||
|
|
||||||
def clean_email(self):
|
|
||||||
"""
|
|
||||||
Verify if email is unique
|
|
||||||
"""
|
|
||||||
User = compat.get_user_model()
|
|
||||||
if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE:
|
|
||||||
if User.objects.filter(email__iexact=self.cleaned_data['email']):
|
|
||||||
raise ValidationError(_('This email address is already in '
|
|
||||||
'use. Please supply a different email address.'))
|
|
||||||
return self.cleaned_data['email']
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""
|
"""
|
||||||
Verifiy that the values entered into the two password fields
|
Verifiy that the values entered into the two password fields
|
||||||
|
@ -105,6 +152,33 @@ class RegistrationForm(forms.UserAttributeFormMixin, Form):
|
||||||
raise ValidationError(_("The two password fields didn't match."))
|
raise ValidationError(_("The two password fields didn't match."))
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
user_fields = {}
|
||||||
|
for field in compat.get_registration_fields():
|
||||||
|
# save User model fields
|
||||||
|
try:
|
||||||
|
User._meta.get_field(field)
|
||||||
|
except FieldDoesNotExist:
|
||||||
|
continue
|
||||||
|
if field.startswith('password'):
|
||||||
|
continue
|
||||||
|
user_fields[field] = kwargs[field]
|
||||||
|
if field == 'email':
|
||||||
|
user_fields[field] = BaseUserManager.normalize_email(kwargs[field])
|
||||||
|
|
||||||
|
new_user = User(is_active=True, **user_fields)
|
||||||
|
new_user.clean()
|
||||||
|
new_user.set_password(kwargs['password1'])
|
||||||
|
new_user.save()
|
||||||
|
|
||||||
|
if app_settings.A2_REGISTRATION_GROUPS:
|
||||||
|
groups = []
|
||||||
|
for name in app_settings.A2_REGISTRATION_GROUPS:
|
||||||
|
group, created = Group.objects.get_or_create(name=name)
|
||||||
|
groups.append(group)
|
||||||
|
new_user.groups = groups
|
||||||
|
return new_user, kwargs['next_url']
|
||||||
|
|
||||||
class SetPasswordForm(auth_forms.SetPasswordForm):
|
class SetPasswordForm(auth_forms.SetPasswordForm):
|
||||||
new_password1 = CharField(label=_("New password"),
|
new_password1 = CharField(label=_("New password"),
|
||||||
widget=PasswordInput,
|
widget=PasswordInput,
|
||||||
|
|
|
@ -1,71 +1,38 @@
|
||||||
from django.conf.urls import patterns
|
from django.conf.urls import patterns
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
from django.contrib.auth import views as auth_views
|
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
from .views import RegistrationView, RegistrationCompletionView, DeleteView,\
|
||||||
|
LoginView, RegistrationCreateView
|
||||||
|
|
||||||
from .. import app_settings
|
urlpatterns = patterns('',
|
||||||
|
url(r'^activate/expired/$',
|
||||||
from registration.backends.default.views import ActivationView
|
TemplateView.as_view(template_name='registration/activation_expired.html'),
|
||||||
from .. import decorators
|
name='registration_activation_expired'),
|
||||||
|
url(r'^activate/failed/$',
|
||||||
def get_form_class(form_class):
|
TemplateView.as_view(template_name='registration/activation_failed.html'),
|
||||||
module, form_class = form_class.rsplit('.', 1)
|
name='registration_activation_failed'),
|
||||||
module = import_module(module)
|
url(r'^activate/(?P<registration_token>[\w:-]+)/$',
|
||||||
return getattr(module, form_class)
|
RegistrationCompletionView.as_view(),
|
||||||
|
name='registration_activate'),
|
||||||
|
url(r'^create/(?P<registration_token>[\w:-]+)/$',
|
||||||
SET_PASSWORD_FORM_CLASS = get_form_class(
|
RegistrationCreateView.as_view(),
|
||||||
app_settings.A2_REGISTRATION_SET_PASSWORD_FORM_CLASS)
|
name='registration_create'),
|
||||||
CHANGE_PASSWORD_FORM_CLASS = get_form_class(
|
url(r'^activate/(?P<registration_token>[\w:-]+)/(?P<username>\w+)$',
|
||||||
app_settings.A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS)
|
LoginView.as_view(),
|
||||||
|
name='registration_login'),
|
||||||
|
url(r'^register/$',
|
||||||
password_change_view = decorators.setting_enabled(
|
RegistrationView.as_view(),
|
||||||
'A2_REGISTRATION_CAN_CHANGE_PASSWORD')(auth_views.password_change)
|
name='registration_register'),
|
||||||
|
url(r'^register/complete/$',
|
||||||
|
TemplateView.as_view(template_name='registration/registration_complete.html'),
|
||||||
urlpatterns = patterns('authentic2.registration_backend.views',
|
name='registration_complete'),
|
||||||
url(r'^activate/complete/$',
|
url(r'^register/closed/$',
|
||||||
TemplateView.as_view(template_name='registration/activation_complete.html'),
|
TemplateView.as_view(template_name='registration/registration_closed.html'),
|
||||||
name='registration_activation_complete'),
|
name='registration_disallowed'),
|
||||||
# Activation keys get matched by \w+ instead of the more specific
|
url(r'^delete/$',
|
||||||
# [a-fA-F0-9]{40} because a bad activation key should still get to the view;
|
login_required(DeleteView.as_view()),
|
||||||
# that way it can return a sensible "invalid key" message instead of a
|
name='delete_account'),
|
||||||
# confusing 404.
|
)
|
||||||
url(r'^activate/(?P<activation_key>\w+)/$',
|
|
||||||
ActivationView.as_view(),
|
|
||||||
name='registration_activate'),
|
|
||||||
url(r'^register/$',
|
|
||||||
'register',
|
|
||||||
name='registration_register'),
|
|
||||||
url(r'^register/complete/$',
|
|
||||||
TemplateView.as_view(template_name='registration/registration_complete.html'),
|
|
||||||
name='registration_complete'),
|
|
||||||
url(r'^register/closed/$',
|
|
||||||
TemplateView.as_view(template_name='registration/registration_closed.html'),
|
|
||||||
name='registration_disallowed'),
|
|
||||||
url(r'^password/change/$', password_change_view,
|
|
||||||
{'password_change_form': CHANGE_PASSWORD_FORM_CLASS},
|
|
||||||
name='auth_password_change'),
|
|
||||||
url(r'^password/change/done/$',
|
|
||||||
auth_views.password_change_done,
|
|
||||||
name='auth_password_change_done'),
|
|
||||||
url(r'^password/reset/$',
|
|
||||||
auth_views.password_reset,
|
|
||||||
name='auth_password_reset'),
|
|
||||||
url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
|
||||||
auth_views.password_reset_confirm,
|
|
||||||
{'set_password_form': SET_PASSWORD_FORM_CLASS},
|
|
||||||
name='auth_password_reset_confirm'),
|
|
||||||
url(r'^password/reset/complete/$',
|
|
||||||
auth_views.password_reset_complete,
|
|
||||||
name='auth_password_reset_complete'),
|
|
||||||
url(r'^password/reset/done/$',
|
|
||||||
auth_views.password_reset_done,
|
|
||||||
name='auth_password_reset_done'),
|
|
||||||
url(r'^delete/$',
|
|
||||||
'delete',
|
|
||||||
name='delete_account'),
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,104 +1,117 @@
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth import authenticate, login as django_login, logout
|
||||||
from django.contrib.sites.models import RequestSite
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.contrib.auth.models import BaseUserManager, Group
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db.models import FieldDoesNotExist
|
from django.db.models import FieldDoesNotExist
|
||||||
|
from django.db import IntegrityError
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.core import signing
|
||||||
|
from django.views.generic.edit import FormView
|
||||||
|
from django.views.generic.base import TemplateView, View
|
||||||
|
|
||||||
|
from authentic2.utils import get_form_class
|
||||||
from registration.views import RegistrationView as BaseRegistrationView
|
|
||||||
from registration.models import RegistrationProfile
|
|
||||||
from registration import signals
|
|
||||||
|
|
||||||
from .. import models, app_settings, compat
|
from .. import models, app_settings, compat
|
||||||
from . import urls
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
User = compat.get_user_model()
|
||||||
|
|
||||||
class RegistrationView(BaseRegistrationView):
|
def valid_token(method):
|
||||||
form_class = urls.get_form_class(app_settings.A2_REGISTRATION_FORM_CLASS)
|
def f(obj, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
registration_kwargs = signing.loads(kwargs['registration_token'],
|
||||||
|
max_age=settings.ACCOUNT_ACTIVATION_DAYS*3600*24)
|
||||||
|
params = kwargs.copy()
|
||||||
|
params.update(registration_kwargs)
|
||||||
|
except signing.SignatureExpired:
|
||||||
|
return redirect('registration_activation_expired')
|
||||||
|
except signing.BadSignature:
|
||||||
|
return redirect('registration_activation_failed')
|
||||||
|
return method(obj, *args, **params)
|
||||||
|
return f
|
||||||
|
|
||||||
def register(self, request, **cleaned_data):
|
def login(request, user, redirect_url='auth_homepage'):
|
||||||
User = compat.get_user_model()
|
user.backend = 'authentic2.backends.ModelBackend'
|
||||||
if Site._meta.installed:
|
django_login(request, user)
|
||||||
site = Site.objects.get_current()
|
return redirect(redirect_url)
|
||||||
else:
|
|
||||||
site = RequestSite(request)
|
class LoginView(View):
|
||||||
user_fields = {}
|
redirect_url = 'auth_homepage'
|
||||||
for field in compat.get_registration_fields():
|
|
||||||
# save User model fields
|
@valid_token
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(email=kwargs['email'], username=kwargs['username'])
|
||||||
|
return login(request, user)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return redirect(self.redirect_url)
|
||||||
|
|
||||||
|
class RegistrationView(FormView):
|
||||||
|
form_class = get_form_class(app_settings.A2_REGISTRATION_FORM_CLASS)
|
||||||
|
template_name = 'registration/registration_form.html'
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.save(self.request)
|
||||||
|
return redirect('registration_complete')
|
||||||
|
|
||||||
|
class RegistrationCompletionView(FormView):
|
||||||
|
form_class = get_form_class(app_settings.A2_REGISTRATION_COMPLETION_FORM_CLASS)
|
||||||
|
http_method_names = ['get', 'post']
|
||||||
|
template_name = 'registration/registration_completion_form.html'
|
||||||
|
|
||||||
|
@valid_token
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE:
|
||||||
try:
|
try:
|
||||||
User._meta.get_field(field)
|
user = User.objects.get(email__iexact=kwargs['email'])
|
||||||
except FieldDoesNotExist:
|
except User.DoesNotExist:
|
||||||
continue
|
return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
|
||||||
if field.startswith('password'):
|
return login(request, user)
|
||||||
continue
|
else:
|
||||||
user_fields[field] = cleaned_data[field]
|
user_accounts = User.objects.filter(email__iexact=kwargs['email'])
|
||||||
if field == 'email':
|
if user_accounts:
|
||||||
user_fields[field] = BaseUserManager.normalize_email(user_fields[field])
|
logout(request)
|
||||||
new_user = User(is_active=False, **user_fields)
|
context = kwargs.copy()
|
||||||
new_user.clean()
|
context.update({'accounts': user_accounts})
|
||||||
new_user.set_password(cleaned_data['password1'])
|
self.template_name = 'registration/login_choices.html'
|
||||||
new_user.save()
|
return self.render_to_response(context)
|
||||||
attributes = models.Attribute.objects.filter(
|
else:
|
||||||
asked_on_registration=True)
|
return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
|
||||||
if attributes:
|
|
||||||
for attribute in attributes:
|
|
||||||
attribute.set_value(new_user, cleaned_data[attribute.name])
|
|
||||||
if app_settings.A2_REGISTRATION_GROUPS:
|
|
||||||
groups = []
|
|
||||||
for name in app_settings.A2_REGISTRATION_GROUPS:
|
|
||||||
group, created = Group.objects.get_or_create(name=name)
|
|
||||||
groups.append(group)
|
|
||||||
new_user.groups = groups
|
|
||||||
registration_profile = RegistrationProfile.objects.create_profile(new_user)
|
|
||||||
registration_profile.send_activation_email(site)
|
|
||||||
|
|
||||||
signals.user_registered.send(sender=self.__class__,
|
@valid_token
|
||||||
user=new_user,
|
def post(self, request, *args, **kwargs):
|
||||||
request=request)
|
form = self.get_form(self.form_class)
|
||||||
return new_user
|
if form.is_valid():
|
||||||
|
params = form.cleaned_data.copy()
|
||||||
|
params.update(kwargs)
|
||||||
|
user, next_url = form.save(**params)
|
||||||
|
if next_url:
|
||||||
|
return login(request, user, next_url)
|
||||||
|
return login(request, user)
|
||||||
|
else:
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
def registration_allowed(self, request):
|
class RegistrationCreateView(RegistrationCompletionView):
|
||||||
"""
|
|
||||||
Indicate whether account registration is currently permitted,
|
|
||||||
based on the value of the setting ``REGISTRATION_OPEN``. This
|
|
||||||
is determined as follows:
|
|
||||||
|
|
||||||
* If ``REGISTRATION_OPEN`` is not specified in settings, or is
|
@valid_token
|
||||||
set to ``True``, registration is permitted.
|
def get(self, request, *args, **kwargs):
|
||||||
|
return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
* If ``REGISTRATION_OPEN`` is both specified and set to
|
class DeleteView(TemplateView):
|
||||||
``False``, registration is not permitted.
|
def get(self, request, *args, **kwargs):
|
||||||
|
next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER')\
|
||||||
"""
|
or request.GET.get('next_url'))
|
||||||
return getattr(settings, 'REGISTRATION_OPEN', True)
|
if not app_settings.A2_REGISTRATION_CAN_DELETE_ACCOUNT:
|
||||||
|
return redirect(next_url)
|
||||||
|
return render(request, 'registration/delete_account.html')
|
||||||
|
|
||||||
def get_success_url(self, request, user):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER')\
|
||||||
Return the name of the URL to redirect to after successful
|
or request.GET.get('next_url'))
|
||||||
user registration.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return ('registration_complete', (), {})
|
|
||||||
|
|
||||||
register = RegistrationView.as_view()
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def delete(request, next_url='/'):
|
|
||||||
next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER') or next_url)
|
|
||||||
if not app_settings.A2_REGISTRATION_CAN_DELETE_ACCOUNT:
|
|
||||||
return redirect(next_url)
|
|
||||||
if request.method == 'POST':
|
|
||||||
if 'submit' in request.POST:
|
if 'submit' in request.POST:
|
||||||
models.DeletedUser.objects.delete_user(request.user)
|
models.DeletedUser.objects.delete_user(request.user)
|
||||||
logger.info(u'deletion of account %s requested' % request.user)
|
logger.info(u'deletion of account %s requested' % request.user)
|
||||||
|
@ -106,4 +119,3 @@ def delete(request, next_url='/'):
|
||||||
return redirect('auth_logout')
|
return redirect('auth_logout')
|
||||||
else:
|
else:
|
||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
return render(request, 'registration/delete_account.html')
|
|
||||||
|
|
|
@ -180,7 +180,6 @@ INSTALLED_APPS = (
|
||||||
'admin_tools.menu',
|
'admin_tools.menu',
|
||||||
'admin_tools.dashboard',
|
'admin_tools.dashboard',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'registration',
|
|
||||||
'django_select2',
|
'django_select2',
|
||||||
'django_tables2',
|
'django_tables2',
|
||||||
'authentic2.nonce',
|
'authentic2.nonce',
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
<p>→ {% trans "Forgot password?" %} <a href="{% url 'auth_password_reset' %}">{% trans "Reset it!" %}</a></p>
|
<p>→ {% trans "Forgot password?" %} <a href="{% url 'auth_password_reset' %}">{% trans "Reset it!" %}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if registration_authorized %}
|
{% if registration_authorized %}
|
||||||
<p>→ {% trans "Not a member?" %} <a href="{% url 'registration_register' %}">{% trans "Register!" %}</a></p>
|
<p>→ {% trans "Not a member?" %} <a href="{% url 'registration_register' %}?{{ request.GET.urlencode }}">{% trans "Register!" %}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans "Account activation" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if account %}
|
|
||||||
|
|
||||||
<p>{% trans "Account successfully activated" %}</p>
|
|
||||||
|
|
||||||
<p><a href="{% url 'auth_login' %}">{% trans "Log in" %}</a></p>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
|
|
||||||
<p>{% trans "Account activation failed" %}</p>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{% trans "Your account is now activated" %}
|
|
||||||
{% endblock %}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<h3>{% trans "Account creation" %}</h3>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}
|
||||||
|
To continue your account creation on {{ site }} please <a href="{{ site }}{{ registration_token }}">click here</a>
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p>{% blocktrans %}This link is valid for {{ expiration_days }} days.{% endblocktrans %}</p>
|
|
@ -1,6 +1,6 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% trans "Activate account at" %} {{ site.name }}:
|
{% trans "Activate account" %}:
|
||||||
|
|
||||||
http://{{ site.domain }}{% url 'registration_activate' activation_key %}
|
{{ registration_url }}
|
||||||
|
|
||||||
{% blocktrans %}Link is valid for {{ expiration_days }} days.{% endblocktrans %}
|
{% blocktrans %}Link is valid for {{ expiration_days }} days.{% endblocktrans %}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{% load i18n %}{% trans "Account activation on" %} {{ site.name }}
|
{% load i18n %}{% trans "Account activation on" %} {{ site }}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% trans "Account activation expired" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{% trans "Account activation" %}</h2>
|
||||||
|
<p>{% trans "Your activation key is expired" %}</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% trans "Account activation failed" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{% trans "Account activation" %}</h2>
|
||||||
|
<p>{% trans "Activation failed" %}</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load breadcrumbs i18n %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% trans "Login" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% breadcrumb_url 'Register' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>{% trans "Login" %}</h2>
|
||||||
|
<p>{% trans "Multiple accounts are associated to this email." %}
|
||||||
|
{% trans "Please choose the account you want to log in with:" %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for account in accounts %}
|
||||||
|
<li><a href="{% url "registration_login" registration_token account.username %}">{{ account }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -6,6 +6,6 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>{% trans "You are now registered. Activation email sent." %}</p>
|
<p>{% trans "Thank you for registering. Activation email sent." %}</p>
|
||||||
<p><a href="/">{% trans "Back" %}</a></p>
|
<p><a href="/">{% trans "Back" %}</a></p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% trans "Registration" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% load breadcrumbs %}
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% breadcrumb_url 'Register' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>{% trans "Registration" %}</h2>
|
||||||
|
<p>{% trans "Please fill the formm to complete your registration" %}</p>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" value="{% trans 'Submit' %}" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
<h2>{% trans "Registration" %}</h2>
|
<h2>{% trans "Registration" %}</h2>
|
||||||
|
|
||||||
<form method="post" action=".">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
from django.test import TestCase
|
import re
|
||||||
|
|
||||||
|
from django.core import mail
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import Client
|
||||||
|
from django.test.utils import override_settings
|
||||||
from django.contrib.auth.hashers import check_password
|
from django.contrib.auth.hashers import check_password
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from . import hashers
|
from . import hashers
|
||||||
|
|
||||||
|
@ -88,3 +94,36 @@ class SerializerTests(TestCase):
|
||||||
self.assertEqual(User.objects.count(), 1)
|
self.assertEqual(User.objects.count(), 1)
|
||||||
self.assertEqual(Attribute.objects.count(), 1)
|
self.assertEqual(Attribute.objects.count(), 1)
|
||||||
self.assertEqual(AttributeValue.objects.count(), 1)
|
self.assertEqual(AttributeValue.objects.count(), 1)
|
||||||
|
|
||||||
|
class RegistrationTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_registration(self):
|
||||||
|
response = self.client.post(reverse('registration_register'),
|
||||||
|
{'email': 'testbot@entrouvert.com'})
|
||||||
|
self.assertRedirects(response, reverse('registration_complete'))
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
links = re.findall('http[s]://.*/', mail.outbox[0].body)
|
||||||
|
self.assertIsInstance(links, list) and self.assertIsNot(links, [])
|
||||||
|
link = links[0]
|
||||||
|
completion = self.client.get(link)
|
||||||
|
self.assertEqual(completion.status_code, 200)
|
||||||
|
self.bad_password_test(link)
|
||||||
|
self.good_password_test(link)
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
def bad_password_test(self, url):
|
||||||
|
"""
|
||||||
|
test short filled password
|
||||||
|
"""
|
||||||
|
completion = self.client.post(url, {'username': 'toto',
|
||||||
|
'password1': 'toto',
|
||||||
|
'password2': 'toto'})
|
||||||
|
self.assertEqual(completion.status_code, 200)
|
||||||
|
|
||||||
|
def good_password_test(self, url):
|
||||||
|
completion = self.client.post(url, {'username': 'toto',
|
||||||
|
'password1': 'T0toto',
|
||||||
|
'password2': 'T0toto'})
|
||||||
|
self.assertEqual(completion.status_code, 302)
|
||||||
|
|
|
@ -206,3 +206,8 @@ def field_names(list_of_field_name_and_titles):
|
||||||
yield t
|
yield t
|
||||||
else:
|
else:
|
||||||
yield t[0]
|
yield t[0]
|
||||||
|
|
||||||
|
def get_form_class(form_class):
|
||||||
|
module, form_class = form_class.rsplit('.', 1)
|
||||||
|
module = import_module(module)
|
||||||
|
return getattr(module, form_class)
|
||||||
|
|
|
@ -19,7 +19,3 @@ except ImportError:
|
||||||
raise
|
raise
|
||||||
print 'django_authopenid is missing: easy_install django-authopenid'
|
print 'django_authopenid is missing: easy_install django-authopenid'
|
||||||
|
|
||||||
try:
|
|
||||||
import registration
|
|
||||||
except ImportError:
|
|
||||||
print 'registration is missing: easy_install django-registration'
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ Django<1.6
|
||||||
south>=0.8.4
|
south>=0.8.4
|
||||||
requests
|
requests
|
||||||
django-model-utils
|
django-model-utils
|
||||||
django-registration>=1
|
|
||||||
django-debug-toolbar>=1.2,<1.3
|
django-debug-toolbar>=1.2,<1.3
|
||||||
--allow-external django-admin-tools
|
--allow-external django-admin-tools
|
||||||
--allow-unverified django-admin-tools
|
--allow-unverified django-admin-tools
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -117,7 +117,6 @@ setup(name="authentic2",
|
||||||
'south>=0.8.4',
|
'south>=0.8.4',
|
||||||
'requests',
|
'requests',
|
||||||
'django-model-utils',
|
'django-model-utils',
|
||||||
'django-registration>=1',
|
|
||||||
'django-admin-tools>=0.5.1',
|
'django-admin-tools>=0.5.1',
|
||||||
'dnspython',
|
'dnspython',
|
||||||
'django-select2',
|
'django-select2',
|
||||||
|
|
Loading…
Reference in New Issue