From 49807d33d92c698acfc88851acb07082e52a3334 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 05:17:28 +0200 Subject: [PATCH] Use a separate app for registration --- apps/member/forms.py | 18 +--- apps/member/models.py | 1 - apps/member/urls.py | 5 -- apps/member/views.py | 100 +-------------------- apps/registration/__init__.py | 4 + apps/registration/api/__init__.py | 0 apps/registration/apps.py | 10 +++ apps/registration/forms.py | 21 +++++ apps/registration/migrations/__init__.py | 0 apps/registration/urls.py | 13 +++ apps/registration/views.py | 110 +++++++++++++++++++++++ note_kfet/settings/base.py | 7 +- note_kfet/urls.py | 8 +- templates/base.html | 4 +- 14 files changed, 168 insertions(+), 133 deletions(-) create mode 100644 apps/registration/__init__.py create mode 100644 apps/registration/api/__init__.py create mode 100644 apps/registration/apps.py create mode 100644 apps/registration/forms.py create mode 100644 apps/registration/migrations/__init__.py create mode 100644 apps/registration/urls.py create mode 100644 apps/registration/views.py diff --git a/apps/member/forms.py b/apps/member/forms.py index e6e73612..5b20fd15 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -2,9 +2,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django import forms -from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput from permission.models import PermissionMask @@ -19,21 +18,6 @@ class CustomAuthenticationForm(AuthenticationForm): ) -class SignUpForm(UserCreationForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields['username'].widget.attrs.pop("autofocus", None) - self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"}) - self.fields['first_name'].required = True - self.fields['last_name'].required = True - self.fields['email'].required = True - self.fields['email'].help_text = _("This address must be valid.") - - class Meta: - model = User - fields = ('first_name', 'last_name', 'username', 'email', ) - - class ProfileForm(forms.ModelForm): """ A form for the extras field provided by the :model:`member.Profile` model. diff --git a/apps/member/models.py b/apps/member/models.py index 462b23fd..294643af 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -12,7 +12,6 @@ from django.urls import reverse, reverse_lazy from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.translation import gettext_lazy as _ - from member.tokens import account_activation_token from note.models import MembershipTransaction diff --git a/apps/member/urls.py b/apps/member/urls.py index 9b6ccbd5..37ff8d2b 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -7,11 +7,6 @@ from . import views app_name = 'member' urlpatterns = [ - path('signup/', views.UserCreateView.as_view(), name="signup"), - path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'), - path('accounts/activate//', views.UserActivateView.as_view(), name='account_activation'), - - path('club/', views.ClubListView.as_view(), name="club_list"), path('club/create/', views.ClubCreateView.as_view(), name="club_create"), path('club//', views.ClubDetailView.as_view(), name="club_detail"), diff --git a/apps/member/views.py b/apps/member/views.py index 0d128394..ac5dd59e 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -12,12 +12,9 @@ from django.contrib.auth.views import LoginView from django.core.exceptions import ValidationError from django.db.models import Q from django.forms import HiddenInput -from django.shortcuts import redirect, resolve_url +from django.shortcuts import redirect from django.urls import reverse_lazy -from django.utils.decorators import method_decorator -from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ -from django.views.decorators.csrf import csrf_protect from django.views.generic import CreateView, DetailView, UpdateView, TemplateView from django.views.generic.base import View from django.views.generic.edit import FormMixin @@ -30,10 +27,9 @@ from note.tables import HistoryTable, AliasTable from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin -from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm +from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm from .models import Club, Membership from .tables import ClubTable, UserTable, MembershipTable -from .tokens import account_activation_token class CustomLoginView(LoginView): @@ -44,98 +40,6 @@ class CustomLoginView(LoginView): return super().form_valid(form) -class UserCreateView(CreateView): - """ - Une vue pour inscrire un utilisateur et lui créer un profil - """ - - form_class = SignUpForm - success_url = reverse_lazy('member:login') - template_name = 'member/signup.html' - second_form = ProfileForm - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["profile_form"] = self.second_form() - - return context - - def form_valid(self, form): - """ - If the form is valid, then the user is created with is_active set to False - so that the user cannot log in until the email has been validated. - """ - profile_form = ProfileForm(self.request.POST) - if not profile_form.is_valid(): - return self.form_invalid(form) - - user = form.save(commit=False) - user.is_active = False - user.profile = profile_form.save(commit=False) - user.save() - user.profile.save() - - user.profile.send_email_validation_link() - - return super().form_valid(form) - - -class UserActivateView(TemplateView): - title = _("Account Activation") - template_name = 'registration/account_activation_complete.html' - - @method_decorator(csrf_protect) - def dispatch(self, *args, **kwargs): - """ - The dispatch method looks at the request to determine whether it is a GET, POST, etc, - and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed - if not. We chose to check the token in the dispatch method to mimic PasswordReset from - django.contrib.auth - """ - assert 'uidb64' in kwargs and 'token' in kwargs - - self.validlink = False - user = self.get_user(kwargs['uidb64']) - token = kwargs['token'] - - if user is not None and account_activation_token.check_token(user, token): - self.validlink = True - user.is_active = True - user.profile.email_confirmed = True - user.save() - return super().dispatch(*args, **kwargs) - else: - # Display the "Account Activation unsuccessful" page. - return self.render_to_response(self.get_context_data()) - - def get_user(self, uidb64): - print(uidb64) - try: - # urlsafe_base64_decode() decodes to bytestring - uid = urlsafe_base64_decode(uidb64).decode() - user = User.objects.get(pk=uid) - except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError): - user = None - return user - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['login_url'] = resolve_url(settings.LOGIN_URL) - if self.validlink: - context['validlink'] = True - else: - context.update({ - 'title': _('Account Activation unsuccessful'), - 'validlink': False, - }) - return context - - -class UserActivationEmailSentView(TemplateView): - template_name = 'registration/account_activation_email_sent.html' - title = _('Account activation email sent') - - class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = User fields = ['first_name', 'last_name', 'username', 'email'] diff --git a/apps/registration/__init__.py b/apps/registration/__init__.py new file mode 100644 index 00000000..700d9f00 --- /dev/null +++ b/apps/registration/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = 'registration.apps.RegistrationConfig' diff --git a/apps/registration/api/__init__.py b/apps/registration/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/registration/apps.py b/apps/registration/apps.py new file mode 100644 index 00000000..dec89274 --- /dev/null +++ b/apps/registration/apps.py @@ -0,0 +1,10 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class RegistrationConfig(AppConfig): + name = 'registration' + verbose_name = _('registration') diff --git a/apps/registration/forms.py b/apps/registration/forms.py new file mode 100644 index 00000000..3f4063d5 --- /dev/null +++ b/apps/registration/forms.py @@ -0,0 +1,21 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.models import User +from django.utils.translation import gettext_lazy as _ + + +class SignUpForm(UserCreationForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['username'].widget.attrs.pop("autofocus", None) + self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"}) + self.fields['first_name'].required = True + self.fields['last_name'].required = True + self.fields['email'].required = True + self.fields['email'].help_text = _("This address must be valid.") + + class Meta: + model = User + fields = ('first_name', 'last_name', 'username', 'email', ) diff --git a/apps/registration/migrations/__init__.py b/apps/registration/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/registration/urls.py b/apps/registration/urls.py new file mode 100644 index 00000000..e752922c --- /dev/null +++ b/apps/registration/urls.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.urls import path + +from . import views + +app_name = 'registration' +urlpatterns = [ + path('signup/', views.UserCreateView.as_view(), name="signup"), + path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'), + path('accounts/activate//', views.UserActivateView.as_view(), name='account_activation'), +] diff --git a/apps/registration/views.py b/apps/registration/views.py new file mode 100644 index 00000000..f4a26c3b --- /dev/null +++ b/apps/registration/views.py @@ -0,0 +1,110 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.conf import settings +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.shortcuts import resolve_url +from django.urls import reverse_lazy +from django.utils.decorators import method_decorator +from django.utils.http import urlsafe_base64_decode +from django.utils.translation import gettext_lazy as _ +from django.views.decorators.csrf import csrf_protect +from django.views.generic import CreateView, TemplateView +from member.forms import ProfileForm +from member.tokens import account_activation_token + +from .forms import SignUpForm + + +class UserCreateView(CreateView): + """ + Une vue pour inscrire un utilisateur et lui créer un profil + """ + + form_class = SignUpForm + success_url = reverse_lazy('member:login') + template_name = 'member/signup.html' + second_form = ProfileForm + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["profile_form"] = self.second_form() + + return context + + def form_valid(self, form): + """ + If the form is valid, then the user is created with is_active set to False + so that the user cannot log in until the email has been validated. + """ + profile_form = ProfileForm(self.request.POST) + if not profile_form.is_valid(): + return self.form_invalid(form) + + user = form.save(commit=False) + user.is_active = False + user.profile = profile_form.save(commit=False) + user.save() + user.profile.save() + + user.profile.send_email_validation_link() + + return super().form_valid(form) + + +class UserActivateView(TemplateView): + title = _("Account Activation") + template_name = 'registration/account_activation_complete.html' + + @method_decorator(csrf_protect) + def dispatch(self, *args, **kwargs): + """ + The dispatch method looks at the request to determine whether it is a GET, POST, etc, + and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed + if not. We chose to check the token in the dispatch method to mimic PasswordReset from + django.contrib.auth + """ + assert 'uidb64' in kwargs and 'token' in kwargs + + self.validlink = False + user = self.get_user(kwargs['uidb64']) + token = kwargs['token'] + + if user is not None and account_activation_token.check_token(user, token): + self.validlink = True + user.is_active = True + user.profile.email_confirmed = True + user.save() + return super().dispatch(*args, **kwargs) + else: + # Display the "Account Activation unsuccessful" page. + return self.render_to_response(self.get_context_data()) + + def get_user(self, uidb64): + print(uidb64) + try: + # urlsafe_base64_decode() decodes to bytestring + uid = urlsafe_base64_decode(uidb64).decode() + user = User.objects.get(pk=uid) + except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError): + user = None + return user + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['login_url'] = resolve_url(settings.LOGIN_URL) + if self.validlink: + context['validlink'] = True + else: + context.update({ + 'title': _('Account Activation unsuccessful'), + 'validlink': False, + }) + return context + + +class UserActivationEmailSentView(TemplateView): + template_name = 'registration/account_activation_email_sent.html' + title = _('Account activation email sent') + diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 61e5ea51..283f8e56 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -54,13 +54,14 @@ INSTALLED_APPS = [ 'rest_framework.authtoken', # Note apps + 'api', 'activity', + 'logs', 'member', 'note', - 'treasury', 'permission', - 'api', - 'logs', + 'registration', + 'treasury', ] LOGIN_REDIRECT_URL = '/note/transfer/' diff --git a/note_kfet/urls.py b/note_kfet/urls.py index a7afab29..90d44a07 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -16,6 +16,7 @@ urlpatterns = [ # Include project routers path('note/', include('note.urls')), path('accounts/', include('member.urls')), + path('registration/', include('registration.urls')), path('activity/', include('activity.urls')), path('treasury/', include('treasury.urls')), @@ -37,14 +38,7 @@ if "cas_server" in settings.INSTALLED_APPS: # Include CAS Server routers path('cas/', include('cas_server.urls', namespace="cas_server")), ] -if "cas" in settings.INSTALLED_APPS: - from cas import views as cas_views - urlpatterns += [ - # Include CAS Client routers - path('accounts/login/cas/', cas_views.login, name='cas_login'), - path('accounts/logout/cas/', cas_views.logout, name='cas_logout'), - ] if "debug_toolbar" in settings.INSTALLED_APPS: import debug_toolbar urlpatterns = [ diff --git a/templates/base.html b/templates/base.html index 9f131054..8e16a419 100644 --- a/templates/base.html +++ b/templates/base.html @@ -124,7 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% else %} @@ -138,7 +138,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
- {% if not user.profile.email_confirmed %} + {% if user.is_authenticated and not user.profile.email_confirmed %}
{% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}