Projet

Général

Profil

0001-Registration-refactored-email-validation-done-first-.patch

Serghei Mihai, 05 janvier 2015 14:29

Télécharger (40,6 ko)

Voir les différences:

Subject: [PATCH 1/2] Registration refactored: email validation done first and
 registration process finished on profile completion.

django-registration removed
 authentic2/app_settings.py                         |   3 +
 authentic2/auth2_auth/auth2_ssl/views.py           |   8 +-
 authentic2/profile_urls.py                         |  29 ++++
 authentic2/registration_backend/__init__.py        |  60 -------
 authentic2/registration_backend/forms.py           | 120 +++++++++++--
 authentic2/registration_backend/urls.py            |  95 +++-------
 authentic2/registration_backend/views.py           | 191 +++++++++++----------
 authentic2/settings.py                             |   1 -
 authentic2/templates/auth/login_form.html          |   2 +-
 authentic2/templates/registration/activate.html    |  22 ---
 .../registration/activation_complete.html          |   6 -
 .../templates/registration/activation_email.html   |   8 +
 .../templates/registration/activation_email.txt    |   4 +-
 .../registration/activation_email_subject.txt      |   2 +-
 .../templates/registration/activation_expired.html |  11 ++
 .../templates/registration/login_choices.html      |  28 +++
 .../registration/registration_complete.html        |   2 +-
 .../registration/registration_completion_form.html |  23 +++
 .../templates/registration/registration_form.html  |   2 +-
 authentic2/tests.py                                |  41 ++++-
 authentic2/utils.py                                |   5 +
 diagnose.py                                        |   4 -
 requirements.txt                                   |   1 -
 setup.py                                           |   1 -
 24 files changed, 393 insertions(+), 276 deletions(-)
 delete mode 100644 authentic2/templates/registration/activate.html
 delete mode 100644 authentic2/templates/registration/activation_complete.html
 create mode 100644 authentic2/templates/registration/activation_email.html
 create mode 100644 authentic2/templates/registration/activation_expired.html
 create mode 100644 authentic2/templates/registration/login_choices.html
 create mode 100644 authentic2/templates/registration/registration_completion_form.html
authentic2/app_settings.py
90 90
                definition='Root urlconf for the /accounts endpoints'),
91 91
    A2_REGISTRATION_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.RegistrationForm',
92 92
                definition='Default registration form'),
93
    A2_REGISTRATION_COMPLETION_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.RegistrationCompletionForm',
94
                definition='Default registration completion form'),
93 95
    A2_REGISTRATION_SET_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.SetPasswordForm',
94 96
                definition='Default set password form'),
95 97
    A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS = Setting(default='authentic2.registration_backend.forms.PasswordChangeForm',
......
131 133
    A2_PASSWORD_POLICY_MIN_LENGTH=Setting(default=6, definition='Minimum number of characters in a password'),
132 134
    A2_AUTH_PASSWORD_ENABLE=Setting(default=True, definition='Activate login/password authentication', names=('AUTH_PASSWORD',)),
133 135
    PUSH_PROFILE_UPDATES=Setting(default=False, definition='Push profile update to linked services'),
136
    A2_REGISTRATION_ID_NAME=Setting(default='registration_id', definition='Unique registration identifier name'),
134 137
)
135 138

  
136 139
app_settings = AppSettings(default_settings)
authentic2/auth2_auth/auth2_ssl/views.py
19 19
from django.contrib.auth import REDIRECT_FIELD_NAME
20 20

  
21 21

  
22
import registration.views
22
from authentic2.registration_backend.views import RegistrationView
23 23

  
24 24

  
25 25
from authentic2.constants import NONCE_FIELD_NAME
......
188 188
def register(request):
189 189
    '''Registration page for SSL auth without CA'''
190 190
    next_url = request.GET.get(REDIRECT_FIELD_NAME, settings.LOGIN_REDIRECT_URL)
191
    return registration.views.register(request, success_url=next_url,
192
            form_class=functools.partial(forms.RegistrationForm,
193
                request=request))
191
    return RegistrationView.as_view(request, success_url=next_url,
192
                                    form_class=functools.partial(forms.RegistrationForm,
193
                                    request=request))
194 194

  
authentic2/profile_urls.py
1 1
from django.conf.urls import patterns, url
2
from django.contrib.auth import views as auth_views
3

  
4
from authentic2.utils import get_form_class
5
from . import app_settings
6

  
7
SET_PASSWORD_FORM_CLASS = get_form_class(
8
        app_settings.A2_REGISTRATION_SET_PASSWORD_FORM_CLASS)
9
CHANGE_PASSWORD_FORM_CLASS = get_form_class(
10
        app_settings.A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS)
2 11

  
3 12
urlpatterns = patterns('authentic2.views',
4 13
    url(r'^logged-in/$', 'logged_in', name='logged-in'),
......
7 16
    url(r'^change-email/verify/$', 'email_change_verify',
8 17
        name='email-change-verify'),
9 18
    url(r'^$', 'profile', name='account_management'),
19
    url(r'^password/change/$',
20
        auth_views.password_change,
21
        {'password_change_form': CHANGE_PASSWORD_FORM_CLASS},
22
        name='auth_password_change'),
23
    url(r'^password/change/done/$',
24
        auth_views.password_change_done,
25
        name='auth_password_change_done'),
26
    url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
27
        auth_views.password_reset_confirm,
28
        {'set_password_form': SET_PASSWORD_FORM_CLASS},
29
        name='auth_password_reset_confirm'),
30
    url(r'^password/reset/$',
31
        auth_views.password_reset,
32
        name='auth_password_reset'),
33
    url(r'^password/reset/complete/$',
34
        auth_views.password_reset_complete,
35
        name='auth_password_reset_complete'),
36
    url(r'^password/reset/done/$',
37
        auth_views.password_reset_done,
38
        name='auth_password_reset_done'),
10 39
)
authentic2/registration_backend/__init__.py
1
from django.conf import settings
2
from django.template.loader import render_to_string
3

  
4
from registration.models import RegistrationProfile
5

  
6
def send_activation_email(self, site):
7
    """
8
    Send an activation email to the user associated with this
9
    ``RegistrationProfile``.
10
    
11
    The activation email will make use of two templates:
12

  
13
    ``registration/activation_email_subject.txt``
14
        This template will be used for the subject line of the
15
        email. Because it is used as the subject line of an email,
16
        this template's output **must** be only a single line of
17
        text; output longer than one line will be forcibly joined
18
        into only a single line.
19

  
20
    ``registration/activation_email.txt``
21
        This template will be used for the body of the email.
22

  
23
    These templates will each receive the following context
24
    variables:
25

  
26
    ``user``
27
        The new user account
28

  
29
    ``activation_key``
30
        The activation key for the new account.
31

  
32
    ``expiration_days``
33
        The number of days remaining during which the account may
34
        be activated.
35

  
36
    ``site``
37
        An object representing the site on which the user
38
        registered; depending on whether ``django.contrib.sites``
39
        is installed, this may be an instance of either
40
        ``django.contrib.sites.models.Site`` (if the sites
41
        application is installed) or
42
        ``django.contrib.sites.models.RequestSite`` (if
43
        not). Consult the documentation for the Django sites
44
        framework for details regarding these objects' interfaces.
45

  
46
    """
47
    ctx_dict = {'activation_key': self.activation_key,
48
                'user': self.user,
49
                'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
50
                'site': site}
51
    subject = render_to_string('registration/activation_email_subject.txt',
52
                               ctx_dict)
53
    # Email subject *must not* contain newlines
54
    subject = ''.join(subject.splitlines())
55
    
56
    message = render_to_string('registration/activation_email.txt',
57
                               ctx_dict)
58
    
59
    self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
60
RegistrationProfile.send_activation_email = send_activation_email
authentic2/registration_backend/forms.py
1
from uuid import uuid1
2

  
3
from django.conf import settings
1 4
from django.core.exceptions import ValidationError
2 5
from django.utils.translation import ugettext_lazy as _
3 6
from django.forms import Form, CharField, PasswordInput, EmailField
4 7
from django.utils.datastructures import SortedDict
5 8
from django.db.models import FieldDoesNotExist
6 9

  
10
from django.contrib.auth.models import BaseUserManager, Group
7 11
from django.contrib.auth import forms as auth_forms
12
from django.core.mail import send_mail
13
from django.core import signing
14
from django import get_version
15
from django.template.loader import render_to_string
16
from django.core.urlresolvers import reverse
17

  
18
from .. import app_settings, compat, forms, utils,\
19
    validators, widgets, fields, models
8 20

  
9
from .. import app_settings, compat, forms, utils, validators, widgets, fields
21
User = compat.get_user_model()
22
is_django_17 = get_version().startswith('1.7')
23
EXPIRATION = settings.ACCOUNT_ACTIVATION_DAYS
10 24

  
25
registration_id_name = app_settings.A2_REGISTRATION_ID_NAME
11 26

  
12
class RegistrationForm(forms.UserAttributeFormMixin, Form):
27
class RegistrationForm(Form):
28
    error_css_class = 'form-field-error'
29
    required_css_class = 'form-field-required'
30

  
31
    email = EmailField()
32

  
33
    def clean_email(self):
34
        """
35
        Verify if email is unique
36
        """
37
        User = compat.get_user_model()
38
        if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE and \
39
           User.objects.filter(email__iexact=self.cleaned_data['email']).exists():
40
            raise ValidationError(_('This email address is already in '
41
                                    'use. Please supply a different email address.'))
42
        return self.cleaned_data['email']
43

  
44
    def save(self, request):
45
        data = self.cleaned_data
46
        registration_id = uuid1().hex
47
        data.update({'next_url': request.GET.get('next_url'),
48
                     registration_id_name: registration_id})
49
        registration_token = signing.dumps(data)
50
        ctx_dict = {'registration_url': request.build_absolute_uri(
51
            reverse('registration_activate',
52
            kwargs={'registration_token': registration_token})),
53
                    'expiration_days': EXPIRATION,
54
                    'email': data['email']}
55
        ctx_dict.update(self.cleaned_data)
56

  
57
        subject = render_to_string('registration/activation_email_subject.txt',
58
                                   ctx_dict)
59

  
60
        subject = ''.join(subject.splitlines())
61
        message = render_to_string('registration/activation_email.txt',
62
                                   ctx_dict)
63
        if is_django_17:
64
            html_message = render_to_string('registration/activation_email.html',
65
                                            ctx_dict)
66
            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
67
                      [data['email']], fail_silently=True,
68
                      html_message=message)
69
        else:
70
            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
71
                      [data['email']], fail_silently=True)
72

  
73
class RegistrationCompletionForm(forms.UserAttributeFormMixin, Form):
13 74
    error_css_class = 'form-field-error'
14 75
    required_css_class = 'form-field-required'
15 76

  
......
21 82
        """
22 83
        Inject required fields in registration form
23 84
        """
24
        super(RegistrationForm, self).__init__(*args, **kwargs)
85
        super(RegistrationCompletionForm, self).__init__(*args, **kwargs)
25 86
        User = compat.get_user_model()
26 87
        insert_idx = 0
27 88
        field_names = compat.get_registration_fields()
......
38 99
                        kwargs['validators'] = model_field.validators
39 100
                    field = model_field.formfield(**kwargs)
40 101
                    if isinstance(field, EmailField):
41
                        field = fields.EmailFieldWithValidation(**kwargs)
102
                        continue
42 103
                    self.fields.insert(insert_idx, field_name, field)
43 104
                    insert_idx += 1
44 105
        for field_name in self.fields:
......
64 125
            self.fields['username'].help_text = app_settings.A2_REGISTRATION_FORM_USERNAME_HELP_TEXT
65 126
            self.fields['username'].label = app_settings.A2_REGISTRATION_FORM_USERNAME_LABEL
66 127

  
128

  
67 129
    def clean_username(self):
68 130
        """
69 131
        Validate that the username is alphanumeric and is not already
......
82 144
        else:
83 145
            return self.cleaned_data['username']
84 146

  
85
    def clean_email(self):
86
        """
87
        Verify if email is unique
88
        """
89
        User = compat.get_user_model()
90
        if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE:
91
            if User.objects.filter(email__iexact=self.cleaned_data['email']):
92
                raise ValidationError(_('This email address is already in '
93
                    'use. Please supply a different email address.'))
94
        return self.cleaned_data['email']
95

  
96 147
    def clean(self):
97 148
        """
98 149
        Verifiy that the values entered into the two password fields
......
105 156
                raise ValidationError(_("The two password fields didn't match."))
106 157
        return self.cleaned_data
107 158

  
159
    def save(self, *args, **kwargs):
160
        user_fields = {}
161
        for field in compat.get_registration_fields():
162
            # save User model fields
163
            try:
164
                User._meta.get_field(field)
165
            except FieldDoesNotExist:
166
                continue
167
            if field.startswith('password'):
168
                continue
169
            user_fields[field] = kwargs[field]
170
            if field == 'email':
171
                user_fields[field] = BaseUserManager.normalize_email(kwargs[field])
172

  
173
        new_user = User(is_active=True, **user_fields)
174
        new_user.clean()
175
        new_user.set_password(kwargs['password1'])
176
        new_user.save()
177

  
178
        registration_id, created = models.Attribute.objects.get_or_create(label=registration_id_name,
179
                                                                          name=registration_id_name,
180
                                                                          kind='string',
181
                                                                          asked_on_registration=False,
182
                                                                          user_visible=False)
183
        registration_id.set_value(new_user, kwargs[registration_id_name])
184

  
185
        attributes = models.Attribute.objects.filter(
186
                asked_on_registration=True)
187
        if attributes:
188
            for attribute in attributes:
189
                attribute.set_value(new_user, kwargs[attribute.name])
190
        if app_settings.A2_REGISTRATION_GROUPS:
191
            groups = []
192
            for name in app_settings.A2_REGISTRATION_GROUPS:
193
                group, created = Group.objects.get_or_create(name=name)
194
                groups.append(group)
195
            new_user.groups = groups
196
        return new_user, kwargs['next_url']
197

  
108 198
class SetPasswordForm(auth_forms.SetPasswordForm):
109 199
    new_password1 = CharField(label=_("New password"),
110 200
                                    widget=PasswordInput,
authentic2/registration_backend/urls.py
1 1
from django.conf.urls import patterns
2 2
from django.conf.urls import url
3 3
from django.utils.importlib import import_module
4
from django.contrib.auth import views as auth_views
5 4
from django.views.generic.base import TemplateView
6

  
7

  
8
from .. import app_settings
9

  
10
from registration.backends.default.views import ActivationView
11
from .. import decorators
12

  
13
def get_form_class(form_class):
14
    module, form_class = form_class.rsplit('.', 1)
15
    module = import_module(module)
16
    return getattr(module, form_class)
17

  
18

  
19
SET_PASSWORD_FORM_CLASS = get_form_class(
20
        app_settings.A2_REGISTRATION_SET_PASSWORD_FORM_CLASS)
21
CHANGE_PASSWORD_FORM_CLASS = get_form_class(
22
        app_settings.A2_REGISTRATION_CHANGE_PASSWORD_FORM_CLASS)
23

  
24

  
25
password_change_view = decorators.setting_enabled(
26
    'A2_REGISTRATION_CAN_CHANGE_PASSWORD')(auth_views.password_change)
27

  
28

  
29
urlpatterns = patterns('authentic2.registration_backend.views',
30
                       url(r'^activate/complete/$',
31
                           TemplateView.as_view(template_name='registration/activation_complete.html'),
32
                           name='registration_activation_complete'),
33
                       # Activation keys get matched by \w+ instead of the more specific
34
                       # [a-fA-F0-9]{40} because a bad activation key should still get to the view;
35
                       # that way it can return a sensible "invalid key" message instead of a
36
                       # confusing 404.
37
                       url(r'^activate/(?P<activation_key>\w+)/$',
38
                           ActivationView.as_view(),
39
                           name='registration_activate'),
40
                       url(r'^register/$',
41
                           'register',
42
                           name='registration_register'),
43
                       url(r'^register/complete/$',
44
                           TemplateView.as_view(template_name='registration/registration_complete.html'),
45
                           name='registration_complete'),
46
                       url(r'^register/closed/$',
47
                           TemplateView.as_view(template_name='registration/registration_closed.html'),
48
                           name='registration_disallowed'),
49
                       url(r'^password/change/$', password_change_view,
50
                           {'password_change_form': CHANGE_PASSWORD_FORM_CLASS},
51
                           name='auth_password_change'),
52
                       url(r'^password/change/done/$',
53
                           auth_views.password_change_done,
54
                           name='auth_password_change_done'),
55
                       url(r'^password/reset/$',
56
                           auth_views.password_reset,
57
                           name='auth_password_reset'),
58
                       url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
59
                           auth_views.password_reset_confirm,
60
                           {'set_password_form': SET_PASSWORD_FORM_CLASS},
61
                           name='auth_password_reset_confirm'),
62
                       url(r'^password/reset/complete/$',
63
                           auth_views.password_reset_complete,
64
                           name='auth_password_reset_complete'),
65
                       url(r'^password/reset/done/$',
66
                           auth_views.password_reset_done,
67
                           name='auth_password_reset_done'),
68
                       url(r'^delete/$',
69
                           'delete',
70
                           name='delete_account'),
71
                       )
5
from django.contrib.auth.decorators import login_required
6

  
7
from .views import RegistrationView, RegistrationCompletionView, DeleteView,\
8
    LoginView
9

  
10
urlpatterns = patterns('',
11
    url(r'^activate/expired/$',
12
        TemplateView.as_view(template_name='registration/activation_expired.html'),
13
        name='registration_activation_expired'),
14
    url(r'^activate/(?P<registration_token>[\w:-]+)/$',
15
        RegistrationCompletionView.as_view(),
16
        name='registration_activate'),
17
    url(r'^activate/(?P<registration_token>[\w:-]+)/(?P<username>\w+)$',
18
        LoginView.as_view(),
19
        name='registration_login'),
20
    url(r'^register/$',
21
        RegistrationView.as_view(),
22
        name='registration_register'),
23
    url(r'^register/complete/$',
24
        TemplateView.as_view(template_name='registration/registration_complete.html'),
25
        name='registration_complete'),
26
    url(r'^register/closed/$',
27
        TemplateView.as_view(template_name='registration/registration_closed.html'),
28
        name='registration_disallowed'),
29
    url(r'^delete/$',
30
        login_required(DeleteView.as_view()),
31
        name='delete_account'),
32
)
authentic2/registration_backend/views.py
1 1
import logging
2
from datetime import datetime
2 3

  
3

  
4
from django.conf import settings
4 5
from django.shortcuts import redirect, render
5 6
from django.utils.translation import ugettext as _
6 7
from django.contrib import messages
7
from django.contrib.auth.decorators import login_required
8
from django.contrib.sites.models import RequestSite
9
from django.contrib.sites.models import Site
10
from django.contrib.auth.models import BaseUserManager, Group
11
from django.conf import settings
8
from django.contrib.auth import authenticate, login as django_login, logout
12 9
from django.db.models import FieldDoesNotExist
10
from django.db import IntegrityError
11
from django.template.loader import render_to_string
12
from django.core import signing
13
from django.views.generic.edit import FormView
14
from django.views.generic.base import TemplateView, View
13 15

  
14

  
15
from registration.views import RegistrationView as BaseRegistrationView
16
from registration.models import RegistrationProfile
17
from registration import signals
18

  
16
from authentic2.utils import get_form_class
19 17
from .. import models, app_settings, compat
20
from . import urls
21

  
18
from .forms import EXPIRATION, registration_id_name
22 19

  
23 20
logger = logging.getLogger(__name__)
24 21

  
25

  
26
class RegistrationView(BaseRegistrationView):
27
    form_class = urls.get_form_class(app_settings.A2_REGISTRATION_FORM_CLASS)
28

  
29
    def register(self, request, **cleaned_data):
30
        User = compat.get_user_model()
31
        if Site._meta.installed:
32
            site = Site.objects.get_current()
33
        else:
34
            site = RequestSite(request)
35
        user_fields = {}
36
        for field in compat.get_registration_fields():
37
            # save User model fields
22
User = compat.get_user_model()
23

  
24
def valid_token(method):
25
    def f(obj, *args, **kwargs):
26
        try:
27
            registration_kwargs = signing.loads(kwargs['registration_token'],
28
                                                max_age=EXPIRATION*3600*24)
29
            params = kwargs.copy()
30
            params.update(registration_kwargs)
31
        except signing.SignatureExpired:
32
            return redirect('registration_activation_expired')
33
        return method(obj, *args, **params)
34
    return f
35

  
36
def login(request, user, redirect_url='auth_homepage'):
37
    user.backend = settings.AUTHENTICATION_BACKENDS[1]
38
    django_login(request, user)
39
    return redirect(redirect_url)
40

  
41
class LoginView(View):
42
    redirect_url = 'auth_homepage'
43

  
44
    @valid_token
45
    def get(self, request, *args, **kwargs):
46
        try:
47
            user = User.objects.get(email=kwargs['email'], username=kwargs['username'])
48
            return login(request, user)
49
        except User.DoesNotExist:
50
            return redirect(self.redirect_url)
51

  
52
class RegistrationView(FormView):
53
    form_class = get_form_class(app_settings.A2_REGISTRATION_FORM_CLASS)
54
    template_name = 'registration/registration_form.html'
55

  
56
    def form_valid(self, form):
57
        form.save(self.request)
58
        return redirect('registration_complete')
59

  
60
class RegistrationCompletionView(FormView):
61
    form_class = get_form_class(app_settings.A2_REGISTRATION_COMPLETION_FORM_CLASS)
62
    http_method_names = ['get', 'post']
63
    template_name = 'registration/registration_completion_form.html'
64

  
65
    @valid_token
66
    def get(self, request, *args, **kwargs):
67
        if app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE:
68
            print "UNIQUE???"
38 69
            try:
39
                User._meta.get_field(field)
40
            except FieldDoesNotExist:
41
                continue
42
            if field.startswith('password'):
43
                continue
44
            user_fields[field] = cleaned_data[field]
45
            if field == 'email':
46
                user_fields[field] = BaseUserManager.normalize_email(user_fields[field])
47
        new_user = User(is_active=False, **user_fields)
48
        new_user.clean()
49
        new_user.set_password(cleaned_data['password1'])
50
        new_user.save()
51
        attributes = models.Attribute.objects.filter(
52
                asked_on_registration=True)
53
        if attributes:
54
            for attribute in attributes:
55
                attribute.set_value(new_user, cleaned_data[attribute.name])
56
        if app_settings.A2_REGISTRATION_GROUPS:
57
            groups = []
58
            for name in app_settings.A2_REGISTRATION_GROUPS:
59
                group, created = Group.objects.get_or_create(name=name)
60
                groups.append(group)
61
            new_user.groups = groups
62
        registration_profile = RegistrationProfile.objects.create_profile(new_user)
63
        registration_profile.send_activation_email(site)
64

  
65
        signals.user_registered.send(sender=self.__class__,
66
                                     user=new_user,
67
                                     request=request)
68
        return new_user
69

  
70
    def registration_allowed(self, request):
71
        """
72
        Indicate whether account registration is currently permitted,
73
        based on the value of the setting ``REGISTRATION_OPEN``. This
74
        is determined as follows:
75

  
76
        * If ``REGISTRATION_OPEN`` is not specified in settings, or is
77
          set to ``True``, registration is permitted.
78

  
79
        * If ``REGISTRATION_OPEN`` is both specified and set to
80
          ``False``, registration is not permitted.
81
        
82
        """
83
        return getattr(settings, 'REGISTRATION_OPEN', True)
84

  
85
    def get_success_url(self, request, user):
86
        """
87
        Return the name of the URL to redirect to after successful
88
        user registration.
89
        
90
        """
91
        return ('registration_complete', (), {})
92

  
93
register = RegistrationView.as_view()
70
                user = User.objects.get(email__iexact=kwargs['email'])
71
            except User.DoesNotExist:
72
                return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
73
            return login(request, user)
74
        else:
75
            user_accounts = User.objects.filter(email__iexact=kwargs['email'])
76
            if user_accounts:
77
                has_active_account = False
78
                for user in user_accounts:
79
                    registration_id = kwargs[registration_id_name]
80
                    if models.AttributeValue.objects.with_owner(user).filter(attribute__label=registration_id_name,
81
                                                                             content='"%s"' % registration_id).exists():
82
                        has_active_account = True
83
                if has_active_account:
84
                    logout(request)
85
                    context = kwargs.copy()
86
                    context.update({'accounts': user_accounts})
87
                    self.template_name = 'registration/login_choices.html'
88
                    return self.render_to_response(context)
89
                else:
90
                    return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
91
            else:
92
                return super(RegistrationCompletionView, self).get(request, *args, **kwargs)
93

  
94
    @valid_token
95
    def post(self, request, *args, **kwargs):
96
        form = self.get_form(self.form_class)
97
        if form.is_valid():
98
            params = form.cleaned_data.copy()
99
            params.update(kwargs)
100
            user, next_url = form.save(**params)
101
            if next_url:
102
                return login(request, user, next_url)
103
            return login(request, user)
104
        else:
105
            return self.form_invalid(form)
94 106

  
107
class DeleteView(TemplateView):
108
    def get(self, request, *args, **kwargs):
109
        next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER')\
110
                                              or request.GET.get('next_url'))
111
        if not app_settings.A2_REGISTRATION_CAN_DELETE_ACCOUNT:
112
            return redirect(next_url)
113
        return render(request, 'registration/delete_account.html')
95 114

  
96
@login_required
97
def delete(request, next_url='/'):
98
    next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER') or next_url)
99
    if not app_settings.A2_REGISTRATION_CAN_DELETE_ACCOUNT:
100
        return redirect(next_url)
101
    if request.method == 'POST':
115
    def post(self, request, *args, **kwargs):
116
        next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER')\
117
                                              or request.GET.get('next_url'))
102 118
        if 'submit' in request.POST:
103 119
            models.DeletedUser.objects.delete_user(request.user)
104 120
            logger.info(u'deletion of account %s requested' % request.user)
......
106 122
            return redirect('auth_logout')
107 123
        else:
108 124
            return redirect(next_url)
109
    return render(request, 'registration/delete_account.html')
authentic2/settings.py
180 180
    'admin_tools.menu',
181 181
    'admin_tools.dashboard',
182 182
    'django.contrib.admin',
183
    'registration',
184 183
    'django_select2',
185 184
    'django_tables2',
186 185
    'authentic2.nonce',
authentic2/templates/auth/login_form.html
15 15
  <p>→ {% trans "Forgot password?" %} <a href="{% url 'auth_password_reset' %}">{% trans "Reset it!" %}</a></p>
16 16
{% endif %}
17 17
{% if registration_authorized %}
18
  <p>→ {% trans "Not a member?" %} <a href="{% url 'registration_register' %}">{% trans "Register!" %}</a></p>
18
  <p>→ {% trans "Not a member?" %} <a href="{% url 'registration_register' %}?{{ request.GET.urlencode }}">{% trans "Register!" %}</a></p>
19 19
{% endif %}
20 20
</div>
authentic2/templates/registration/activate.html
1
{% extends "base.html" %}
2
{% load i18n %}
3

  
4
{% block title %}
5
{% trans "Account activation" %}
6
{% endblock %}
7

  
8
{% block content %}
9

  
10
{% if account %}
11

  
12
<p>{% trans "Account successfully activated" %}</p>
13

  
14
<p><a href="{% url 'auth_login' %}">{% trans "Log in" %}</a></p>
15

  
16
{% else %}
17

  
18
<p>{% trans "Account activation failed" %}</p>
19

  
20
{% endif %}
21

  
22
{% endblock %}
authentic2/templates/registration/activation_complete.html
1
{% extends "base.html" %}
2
{% load i18n %}
3

  
4
{% block content %}
5
{% trans "Your account is now activated" %}
6
{% endblock %}
authentic2/templates/registration/activation_email.html
1
{% load i18n %}
2
<h3>{% trans "Account creation" %}</h3>
3
<p>
4
{% blocktrans %}
5
To continue your account creation on {{ site }} please <a href="{{ site }}{{ registration_token }}">click here</a>
6
{% endblocktrans %}
7
</p>
8
<p>{% blocktrans %}This link is valid for {{ expiration_days }} days.{% endblocktrans %}</p>
authentic2/templates/registration/activation_email.txt
1 1
{% load i18n %}
2
{% trans "Activate account at" %} {{ site.name }}:
2
{% trans "Activate account" %}:
3 3

  
4
http://{{ site.domain }}{% url 'registration_activate' activation_key %}
4
{{ registration_url }}
5 5

  
6 6
{% blocktrans %}Link is valid for {{ expiration_days }} days.{% endblocktrans %}
authentic2/templates/registration/activation_email_subject.txt
1
{% load i18n %}{% trans "Account activation on" %} {{ site.name }}
1
{% load i18n %}{% trans "Account activation on" %} {{ site }}
authentic2/templates/registration/activation_expired.html
1
{% extends "base.html" %}
2
{% load i18n %}
3

  
4
{% block title %}
5
{% trans "Account activation expired" %}
6
{% endblock %}
7

  
8
{% block content %}
9
<h2>{% trans "Account activation" %}</h2>
10
<p>{% trans "Your activation key is expired" %}</p>
11
{% endblock %}
authentic2/templates/registration/login_choices.html
1
{% extends "base.html" %}
2
{% load breadcrumbs i18n %}
3

  
4
{% block title %}
5
{% trans "Login" %}
6
{% endblock %}
7

  
8
{% block breadcrumbs %}
9
{{ block.super }}
10
{% breadcrumb_url 'Register' %}
11
{% endblock %}
12

  
13

  
14
{% block content %}
15

  
16
<h2>{% trans "Login" %}</h2>
17
<p>{% trans "Multiple accounts are associated to this email." %}
18
{% trans "Please choose the account you want to log in with:" %}
19
</p>
20

  
21

  
22
<ul>
23
  {% for account in accounts %}
24
  <li><a href="{% url "registration_login" registration_token account.username %}">{{ account }}</a></li>
25
  {% endfor %}
26
</ul>
27

  
28
{% endblock %}
authentic2/templates/registration/registration_complete.html
6 6
{% endblock %}
7 7

  
8 8
{% block content %}
9
<p>{% trans "You are now registered. Activation email sent." %}</p>
9
<p>{% trans "Thank you for registering. Activation email sent." %}</p>
10 10
<p><a href="/">{% trans "Back" %}</a></p>
11 11
{% endblock %}
authentic2/templates/registration/registration_completion_form.html
1
{% extends "base.html" %}
2
{% load i18n %}
3

  
4
{% block title %}
5
{% trans "Registration" %}
6
{% endblock %}
7

  
8
{% load breadcrumbs %}
9
{% block breadcrumbs %}
10
{{ block.super }}
11
{% breadcrumb_url 'Register' %}
12
{% endblock %}
13

  
14
{% block content %}
15

  
16
<h2>{% trans "Registration" %}</h2>
17
<p>{% trans "Please fill the formm to complete your registration" %}</p>
18
<form method="post">
19
  {% csrf_token %}
20
  {{ form.as_p }}
21
  <input type="submit" value="{% trans 'Submit' %}" />
22
</form>
23
{% endblock %}
authentic2/templates/registration/registration_form.html
15 15

  
16 16
<h2>{% trans "Registration" %}</h2>
17 17

  
18
<form method="post" action=".">
18
<form method="post">
19 19
  {% csrf_token %}
20 20
  {{ form.as_p }}
21 21

  
authentic2/tests.py
1
from django.test import TestCase
1
import re
2 2

  
3
from django.core import mail
4
from django.core.urlresolvers import reverse
5
from django.test import TestCase
6
from django.test.client import Client
7
from django.test.utils import override_settings
3 8
from django.contrib.auth.hashers import check_password
9
from django.conf import settings
4 10

  
5 11
from . import hashers
6 12

  
......
88 94
        self.assertEqual(User.objects.count(), 1)
89 95
        self.assertEqual(Attribute.objects.count(), 1)
90 96
        self.assertEqual(AttributeValue.objects.count(), 1)
97

  
98
class RegistrationTests(TestCase):
99
    def setUp(self):
100
        self.client = Client()
101

  
102
    def test_registration(self):
103
        response = self.client.post(reverse('registration_register'),
104
                                    {'email': 'testbot@entrouvert.com'})
105
        self.assertRedirects(response, reverse('registration_complete'))
106
        self.assertEqual(len(mail.outbox), 1)
107
        links = re.findall('http[s]://.*/', mail.outbox[0].body)
108
        self.assertIsInstance(links, list) and self.assertIsNot(links, [])
109
        link = links[0]
110
        completion = self.client.get(link)
111
        self.assertEqual(completion.status_code, 200)
112
        self.bad_password_test(link)
113
        self.good_password_test(link)
114
        mail.outbox = []
115

  
116
    def bad_password_test(self, url):
117
        """
118
        test short filled password
119
        """
120
        completion = self.client.post(url, {'username': 'toto',
121
                                            'password1': 'toto',
122
                                            'password2': 'toto'})
123
        self.assertEqual(completion.status_code, 200)
124

  
125
    def good_password_test(self, url):
126
        completion = self.client.post(url, {'username': 'toto',
127
                                            'password1': 'T0toto',
128
                                            'password2': 'T0toto'})
129
        self.assertEqual(completion.status_code, 302)
authentic2/utils.py
206 206
            yield t
207 207
        else:
208 208
            yield t[0]
209

  
210
def get_form_class(form_class):
211
    module, form_class = form_class.rsplit('.', 1)
212
    module = import_module(module)
213
    return getattr(module, form_class)
diagnose.py
19 19
    raise
20 20
    print 'django_authopenid is missing: easy_install django-authopenid'
21 21

  
22
try:
23
    import registration
24
except ImportError:
25
    print 'registration is missing: easy_install django-registration'
requirements.txt
2 2
south>=0.8.4
3 3
requests
4 4
django-model-utils
5
django-registration>=1
6 5
django-debug-toolbar>=1.2,<1.3
7 6
--allow-external django-admin-tools
8 7
--allow-unverified django-admin-tools
setup.py
117 117
        'south>=0.8.4',
118 118
        'requests',
119 119
        'django-model-utils',
120
        'django-registration>=1',
121 120
        'django-admin-tools>=0.5.1',
122 121
        'dnspython',
123 122
        'django-select2',
124
-