]> git.parisson.com Git - teleforma.git/commitdiff
WIP password
authorYoan Le Clanche <yoanl@pilotsystems.net>
Tue, 6 Jun 2023 08:36:24 +0000 (10:36 +0200)
committerYoan Le Clanche <yoanl@pilotsystems.net>
Tue, 6 Jun 2023 08:36:24 +0000 (10:36 +0200)
teleforma/templates/registration/password_reset_email.html
teleforma/templates/registration/password_reset_form.html
teleforma/urls.py
teleforma/views/password.py [new file with mode: 0644]

index 715e4fa9eddda236df1f20fd723785a6adaf8fe8..5c83e14758cbe1691ff33b3c1d88b40ca82103d3 100644 (file)
@@ -1,13 +1,13 @@
 {% load teleforma_tags %}{% load i18n %}{% autoescape off %}{% trans "Hello" %},
 
 {% trans "You're receiving this e-mail because you requested a password reset" %}
-{% blocktrans %}for your user account at {{ site_name }}{% endblocktrans %}.
+{% if logins|length > 1 %}pour vos comptes {% for login in logins %}{{login}}{% if not forloop.last %}, {% endif %}{% endfor %}.{% else %}pour votre compte {{logins.0}}.
+{% endif %}
 
 {% trans "Please go to the following page and choose a new password:" %}
 {% block reset_link %}
 {{ protocol }}://{{ domain }}{% url 'teleforma-password-reset-confirm' uidb64=uid token=token %}
 {% endblock %}
-{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
 
 {% trans "Best regards" %},
 {% trans "The site administrator" %} {% trans "of the" %} {% organization %}
index a297f988362985530bd6524148fcb9391ba1141d..22380f9f0a99eb736b0b35ae306ce1c69013bed1 100644 (file)
@@ -4,10 +4,11 @@
 {% block title %}<br />{% trans "Password reset" %}{% endblock %}
 
 {% block content %}
-<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p>
+<p>Vous avez oubliez votre mot de passe ? Veuillez saisir votre adresse e-mail ou votre identifiant de connexion et nous vous enverrons un email avec les instructions pour définir un nouveau mot de passe.
+</p>
 
 <form id="password_reset" action="" method="post">{% csrf_token %}
 {{ form.email.errors }}
-<p><label for="id_email">{% trans 'E-mail address:' %}</label> {{ form.email }} <a href="#" class="component_icon button icon_ok" onclick="document.getElementById('password_reset').submit(); return false;">{% trans 'Reset my password' %}</a></p>
+<p><label for="id_email">Adresse e-mail ou identifiant</label> {{ form.email }} <a href="#" class="component_icon button icon_ok" onclick="document.getElementById('password_reset').submit(); return false;">{% trans 'Reset my password' %}</a></p>
 </form>
 {% endblock %}
index cbb484aade42b88f741cc136c1b41e620d269abc..3f11100fa3f42201301848ebcbf373d12f5ee416 100644 (file)
@@ -43,13 +43,13 @@ from django.contrib.auth.views import (LoginView, LogoutView,
                                        PasswordContextMixin,
                                        PasswordResetCompleteView,
                                        PasswordResetConfirmView,
-                                       PasswordResetDoneView,
-                                       PasswordResetView)
+                                       PasswordResetDoneView)
 from django.views.generic.base import TemplateView
 from django.views.decorators.cache import cache_page
 from jsonrpc import jsonrpc_site
 
 from teleforma.views.home import HomeView
+from teleforma.views.password import TFPasswordResetConfirmView, TFPasswordResetView
 
 from .views.appointment import Appointments, cancel_appointment
 from .views.core import (ChatMessageView, ConferenceListView, ConferenceView, CourseListView,
@@ -121,11 +121,11 @@ urlpatterns = [
     url(r'^accounts/password_change_done/$', PasswordChangeDoneView.as_view(
         template_name='registration/password_change_done.html'), name="password_change_done"),
 
-    url(r'^accounts/password_reset/$', PasswordResetView.as_view(template_name='registration/password_reset_form.html',
+    url(r'^accounts/password_reset/$', TFPasswordResetView.as_view(template_name='registration/password_reset_form.html',
                                                                  email_template_name='registration/password_reset_email.html'), name="teleforma-password-reset"),
     url(r'^accounts/password_reset_done/$', PasswordResetDoneView.as_view(
         template_name='registration/password_reset_done.html'), name="password_reset_done"),
-    path('accounts/password_reset_confirm/<uidb64>/<token>/', PasswordResetConfirmView.as_view(
+    path('accounts/password_reset_confirm/<uidb64>/<token>/', TFPasswordResetConfirmView.as_view(
         template_name='registration/password_reset_confirm.html'), name="teleforma-password-reset-confirm"),
     url(r'^accounts/password_reset_complete/$', PasswordResetCompleteView.as_view(template_name='registration/password_reset_complete.html'),
         name="password_reset_complete"),
diff --git a/teleforma/views/password.py b/teleforma/views/password.py
new file mode 100644 (file)
index 0000000..af7cdaf
--- /dev/null
@@ -0,0 +1,170 @@
+from django import forms
+from django.contrib.auth import get_user_model
+from django.contrib.auth import login as auth_login
+from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
+from django.contrib.auth.models import User
+from django.contrib.auth.tokens import default_token_generator
+from django.contrib.auth.views import (INTERNAL_RESET_SESSION_TOKEN,
+                                       PasswordResetConfirmView,
+                                       PasswordResetView)
+from django.contrib.sites.shortcuts import get_current_site
+from django.db.models import Q
+from django.http import HttpResponseRedirect
+from django.urls import reverse_lazy
+from django.utils.decorators import method_decorator
+from django.utils.encoding import force_bytes
+from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
+from django.utils.translation import gettext_lazy as _
+from django.views.decorators.cache import never_cache
+from django.views.decorators.debug import sensitive_post_parameters
+from django.views.generic.edit import FormView
+
+UserModel = get_user_model()
+
+class TFPasswordResetForm(PasswordResetForm):
+    email = forms.CharField(
+        label="Email ou login",
+        max_length=254,
+    )
+
+    def get_users(self, email):
+        """Given an email, return matching user(s) who should receive a reset.
+
+        This allows subclasses to more easily customize the default policies
+        that prevent inactive users and users with unusable passwords from
+        resetting their password.
+        """
+        return User.objects.filter(Q(is_active=True) & (Q(email=email) | Q(username=email)))
+    def save(self, domain_override=None,
+             subject_template_name='registration/password_reset_subject.txt',
+             email_template_name='registration/password_reset_email.html',
+             use_https=False, token_generator=default_token_generator,
+             from_email=None, request=None, html_email_template_name=None,
+             extra_email_context=None):
+        """
+        Generate a one-use only link for resetting password and send it to the
+        user.
+        """
+        email = self.cleaned_data["email"]
+        if not domain_override:
+            current_site = get_current_site(request)
+            site_name = current_site.name
+            domain = current_site.domain
+        else:
+            site_name = domain = domain_override
+        email_field_name = UserModel.get_email_field_name()
+        logins = [username for username in self.get_users(email)]
+        users = self.get_users(email) 
+        if users:
+            user = self.get_users(email)[0]
+            user_email = getattr(user, email_field_name)
+            context = {
+                'email': user_email,
+                'domain': domain,
+                'site_name': site_name,
+                'uid': urlsafe_base64_encode(force_bytes(user.email)),
+                'user': user,
+                'logins': logins,
+                'token': token_generator.make_token(user),
+                'protocol': 'https' if use_https else 'http',
+                **(extra_email_context or {}),
+            }
+            self.send_mail(
+                subject_template_name, email_template_name, context, from_email,
+                user_email, html_email_template_name=html_email_template_name,
+            )
+
+class TFPasswordResetView(PasswordResetView):
+    form_class = TFPasswordResetForm
+
+
+
+class TFSetPasswordForm(SetPasswordForm):
+    def __init__(self, users, *args, **kwargs):
+        self.users = users
+        super().__init__(*args, **kwargs)
+
+    def save(self, commit=True):
+        password = self.cleaned_data["new_password1"]
+        for user in self.users:
+            user.set_password(password)
+            if commit:
+                user.save()
+        return self.users
+
+
+class TFPasswordResetConfirmView(PasswordResetConfirmView):
+    form_class = TFSetPasswordForm
+    post_reset_login = False
+    post_reset_login_backend = None
+    reset_url_token = 'set-password'
+    success_url = reverse_lazy('password_reset_complete')
+    template_name = 'registration/password_reset_confirm.html'
+    title = _('Enter new password')
+    token_generator = default_token_generator
+
+    @method_decorator(sensitive_post_parameters())
+    @method_decorator(never_cache)
+    def dispatch(self, *args, **kwargs):
+        assert 'uidb64' in kwargs and 'token' in kwargs
+
+        self.validlink = False
+        self.user = None
+        self.users = self.get_users(kwargs['uidb64']) 
+        token = kwargs['token']
+
+        if self.users:
+            if token == self.reset_url_token:
+                session_token = self.request.session.get(INTERNAL_RESET_SESSION_TOKEN)
+
+                check = False                
+                for user in self.users:
+                    check = self.token_generator.check_token(user, session_token)
+                    if check:
+                        break
+                
+                if check:
+                    # If the token is valid, display the password reset form.
+                    self.validlink = True
+                    return super(FormView, self).dispatch(*args, **kwargs)
+            else:
+                check = False
+                for user in self.users:
+                    check = self.token_generator.check_token(user, token)     
+                    if check:
+                        break
+                if check:
+                    # Store the token in the session and redirect to the
+                    # password reset form at a URL without the token. That
+                    # avoids the possibility of leaking the token in the
+                    # HTTP Referer header.
+                    self.request.session[INTERNAL_RESET_SESSION_TOKEN] = token
+                    redirect_url = self.request.path.replace(token, self.reset_url_token)
+                    return HttpResponseRedirect(redirect_url)
+
+        # Display the "Password reset unsuccessful" page.
+        return self.render_to_response(self.get_context_data())
+
+    def get_users(self, uidb64):
+        try:
+            # urlsafe_base64_decode() decodes to bytestring
+            email = urlsafe_base64_decode(uidb64).decode()
+            users = User.objects.filter(email=email)
+        except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist, forms.ValidationError):
+            users = []
+        return users
+    
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs['users'] = self.users
+        return kwargs
+
+    def form_valid(self, form):
+        users = form.save()
+        # get last user
+        user = sorted(users, key=lambda u:u.date_joined)[0]
+        del self.request.session[INTERNAL_RESET_SESSION_TOKEN]
+        if self.post_reset_login:
+            auth_login(self.request, user, self.post_reset_login_backend)
+        return super(FormView, self).form_valid(form)    
\ No newline at end of file