From fc4939577ab5fc180b725d7d5cc6629f11d044b7 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Tue, 16 Dec 2025 10:19:47 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20la=20gestion=20de=20l'activation?= =?UTF-8?q?=20des=20comptes=20utilisateur=20par=20e-mail,=20des=20validati?= =?UTF-8?q?ons=20pour=20les=20pseudos=20existants,=20et=20configuration=20?= =?UTF-8?q?par=20d=C3=A9faut=20de=20l'e-mail=20backend.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devart/settings.py | 5 +++- users/forms.py | 10 +++++++- users/tokens.py | 4 ++++ users/urls.py | 2 ++ users/views.py | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 users/tokens.py diff --git a/devart/settings.py b/devart/settings.py index 2736323..a352b3f 100644 --- a/devart/settings.py +++ b/devart/settings.py @@ -202,4 +202,7 @@ def get_git_version(): else: return "Version inconnue (Fichier manquant)" -GIT_VERSION = get_git_version() \ No newline at end of file +GIT_VERSION = get_git_version() + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +DEFAULT_FROM_EMAIL = 'noreply@partirdezero.local' \ No newline at end of file diff --git a/users/forms.py b/users/forms.py index 2335d4c..362845e 100644 --- a/users/forms.py +++ b/users/forms.py @@ -27,7 +27,15 @@ class UserRegistrationForm(forms.Form): password2 = cleaned_data.get("password2") if password1 and password2 and password1 != password2: - raise forms.ValidationError("Passwords do not match") + raise forms.ValidationError("Les mots de passe ne correspondent pas.") + + return cleaned_data + + def clean_username(self): + username = self.cleaned_data.get('username') + if username and User.objects.filter(username__iexact=username).exists(): + raise forms.ValidationError("Ce pseudo est déjà pris. Veuillez en choisir un autre.") + return username class UserLoginForm(forms.Form): username = forms.CharField( diff --git a/users/tokens.py b/users/tokens.py new file mode 100644 index 0000000..bc82caf --- /dev/null +++ b/users/tokens.py @@ -0,0 +1,4 @@ +from django.contrib.auth.tokens import PasswordResetTokenGenerator + +# Générateur de tokens pour l'activation de compte +activation_token = PasswordResetTokenGenerator() diff --git a/users/urls.py b/users/urls.py index cdf7a52..e46826d 100644 --- a/users/urls.py +++ b/users/urls.py @@ -7,6 +7,8 @@ urlpatterns = [ path('', views.register, name='register'), path('login/', views.login, name='login'), path('logout/', views.logout, name='logout'), + # Activation de compte par lien tokenisé + path('activate///', views.activate, name='activate'), path('profile/view//', views.another_profile, name='another_profile'), path('complete-profile/', views.complete_profile, name='complete_profile'), path('profile/', views.profile, name='profile'), diff --git a/users/views.py b/users/views.py index 7b6ce5e..bf3c51d 100644 --- a/users/views.py +++ b/users/views.py @@ -5,6 +5,11 @@ from django.contrib.auth.models import User from courses.models import Course from .forms import UserRegistrationForm, UserLoginForm, UserUpdateForm, ProfileUpdateForm, CompleteProfileForm from django.contrib.auth.decorators import login_required +from django.core.mail import send_mail +from django.urls import reverse +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode +from django.utils.encoding import force_bytes +from .tokens import activation_token def register(request): # Si l'utilisateur est deja connecté, on le redirige vers la page de profil @@ -13,13 +18,36 @@ def register(request): if request.method == 'POST': form = UserRegistrationForm(request.POST) if form.is_valid(): + # Crée un utilisateur inactif en attente d'activation par e‑mail user = User.objects.create_user( username=form.cleaned_data['username'], email=form.cleaned_data['email'], password=form.cleaned_data['password1'] ) - auth_login(request, user) - return redirect('profile') + user.is_active = False + user.save() + + # Envoi du lien d'activation par e‑mail + uid = urlsafe_base64_encode(force_bytes(user.pk)) + token = activation_token.make_token(user) + activation_link = request.build_absolute_uri( + reverse('activate', kwargs={'uidb64': uid, 'token': token}) + ) + subject = 'Active ton compte sur partirdezero' + message = ( + 'Bienvenue !\n\n' + 'Pour activer ton compte, clique sur le lien suivant (valide pendant une durée limitée) :\n' + f'{activation_link}\n\n' + "Si tu n'es pas à l'origine de cette inscription, ignore ce message." + ) + try: + send_mail(subject, message, None, [user.email], fail_silently=True) + except Exception: + # Même si l'envoi échoue, on n'interrompt pas le flux; un admin vérifiera la config e‑mail + pass + + messages.success(request, "Inscription réussie. Vérifie ta boîte mail pour activer ton compte.") + return redirect('login') else: form = UserRegistrationForm() return render(request, 'users/register.html', {'form': form}) @@ -32,8 +60,15 @@ def login(request): password = form.cleaned_data['password'] user = authenticate(request, username=username, password=password) if user is not None: + if not user.is_active: + messages.error(request, "Votre compte n'est pas encore activé. Consultez l'e‑mail d'activation envoyé." ) + return render(request, 'users/login.html', {'form': form}) auth_login(request, user) return redirect('profile') + else: + # Identifiants invalides: avertir l'utilisateur + messages.error(request, "Nom d'utilisateur ou mot de passe incorrect.") + return render(request, 'users/login.html', {'form': form}) else: form = UserLoginForm() return render(request, 'users/login.html', {'form': form}) @@ -106,3 +141,21 @@ def create_post(request): def another_profile(request, user_id): user = User.objects.get(id=user_id) return render(request, 'users/another_profile.html', {'user': user}) + +# Activation de compte via lien tokenisé +def activate(request, uidb64, token): + try: + uid = urlsafe_base64_decode(uidb64).decode() + user = User.objects.get(pk=uid) + except Exception: + user = None + + if user and activation_token.check_token(user, token): + if not user.is_active: + user.is_active = True + user.save() + messages.success(request, 'Votre compte a été activé. Vous pouvez maintenant vous connecter.') + return redirect('login') + + messages.error(request, "Lien d'activation invalide ou expiré. Demandez un nouveau lien ou inscrivez‑vous à nouveau.") + return redirect('register')