From 6e97e24bf0a8d1cf71c2921cf4a3779663a41457 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Thu, 23 Nov 2023 20:19:39 +0100 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20Creacion=20de=20reseteo=20de=20co?= =?UTF-8?q?ntrase=C3=B1a=20funcional=20a=20falta=20de=20que=20funcione=20e?= =?UTF-8?q?nviar=20el=20email?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/authentication/login.html | 2 + .../authentication/password_reset.html | 35 ++++++++++++++++ .../password_reset_complete.html | 15 +++++++ .../password_reset_confirm.html | 40 +++++++++++++++++++ .../authentication/password_reset_done.html | 15 +++++++ decide/authentication/urls.py | 40 ++++++++++++++++++- 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 decide/authentication/templates/authentication/password_reset.html create mode 100644 decide/authentication/templates/authentication/password_reset_complete.html create mode 100644 decide/authentication/templates/authentication/password_reset_confirm.html create mode 100644 decide/authentication/templates/authentication/password_reset_done.html diff --git a/decide/authentication/templates/authentication/login.html b/decide/authentication/templates/authentication/login.html index 4bf3bcee9..ee8e0e360 100644 --- a/decide/authentication/templates/authentication/login.html +++ b/decide/authentication/templates/authentication/login.html @@ -93,6 +93,8 @@ Continuar con Google + + Recuperar Contraseña {% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset.html b/decide/authentication/templates/authentication/password_reset.html new file mode 100644 index 000000000..fd7813151 --- /dev/null +++ b/decide/authentication/templates/authentication/password_reset.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}Restore password{% endblock %} + +{% block extrahead %} + +{% endblock %} + +{% block content %} +
+

Restaurar Contraseña

+ {% if form.errors %} + {% for field, errors in form.errors.items %} +
+

+ {{ errors|striptags }} +

+
+ {% endfor %} + {% endif %} +
+ {% csrf_token %} +
+
+ + +
+
+
+ +
+
+
+{% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_complete.html b/decide/authentication/templates/authentication/password_reset_complete.html new file mode 100644 index 000000000..6bd10e62c --- /dev/null +++ b/decide/authentication/templates/authentication/password_reset_complete.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}Restore password{% endblock %} + +{% block extrahead %} + +{% endblock %} + +{% block content %} +
+

Restaurar Contraseña

+

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

+
+{% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_confirm.html b/decide/authentication/templates/authentication/password_reset_confirm.html new file mode 100644 index 000000000..f7dbdab31 --- /dev/null +++ b/decide/authentication/templates/authentication/password_reset_confirm.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}Restore password{% endblock %} + +{% block extrahead %} + +{% endblock %} + +{% block content %} +
+

Restaurar Contraseña

+ {% if form.errors %} + {% for field, errors in form.errors.items %} +
+

+ {{ errors|striptags }} +

+
+ {% endfor %} + {% endif %} + +
+ {% csrf_token %} +
+
+ + +
+
+ + +
+
+
+ +
+
+
+{% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_done.html b/decide/authentication/templates/authentication/password_reset_done.html new file mode 100644 index 000000000..a041791b8 --- /dev/null +++ b/decide/authentication/templates/authentication/password_reset_done.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}Restore password{% endblock %} + +{% block extrahead %} + +{% endblock %} + +{% block content %} +
+

Restaurar Contraseña

+

Se ha enviado un correo a dicho email para resetear la contraseña. Revisa tu correo y sigue las instrucciones.

+
+{% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 3a0a3daa5..10b005442 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -1,7 +1,19 @@ from django.urls import path, include from rest_framework.authtoken.views import obtain_auth_token +from django.contrib.auth.views import ( + PasswordResetView, + PasswordResetDoneView, + PasswordResetConfirmView, + PasswordResetCompleteView, +) -from .views import GetUserView, LoginView, LogoutView, RegisterView, ChangePasswordView +from .views import ( + GetUserView, + LoginView, + LogoutView, + RegisterView, + ChangePasswordView, +) urlpatterns = [ path("login/", obtain_auth_token), @@ -11,4 +23,30 @@ path("accounts/", include("allauth.urls")), path("register/", RegisterView.as_view(), name="register"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), + path( + "restore-password/", + PasswordResetView.as_view(template_name="authentication/password_reset.html"), + name="password_reset", + ), + path( + "restore-password/done/", + PasswordResetDoneView.as_view( + template_name="authentication/password_reset_done.html" + ), + name="password_reset_done", + ), + path( + "reset///", + PasswordResetConfirmView.as_view( + template_name="authentication/password_reset_confirm.html" + ), + name="password_reset_confirm", + ), + path( + "reset/done/", + PasswordResetCompleteView.as_view( + template_name="authentication/password_reset_complete.html" + ), + name="password_reset_complete", + ), ] From 3674a7bc03eba4a1fa5dc285736b3c9b90715451 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Thu, 14 Dec 2023 09:46:11 +0100 Subject: [PATCH 02/16] feat: saving changes for reviewing --- .../templates/authentication/login.html | 2 +- .../authentication/password_reset.html | 16 +++- .../authentication/password_reset_done.html | 8 +- decide/authentication/urls.py | 3 + decide/authentication/views.py | 80 +++++++++++++++++++ decide/decide/settings.py | 6 ++ requirements.txt | 57 ++++++++++--- 7 files changed, 157 insertions(+), 15 deletions(-) diff --git a/decide/authentication/templates/authentication/login.html b/decide/authentication/templates/authentication/login.html index 92211b700..70457c46c 100644 --- a/decide/authentication/templates/authentication/login.html +++ b/decide/authentication/templates/authentication/login.html @@ -102,7 +102,7 @@ - Recuperar Contraseña + Recuperar Contraseña {% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset.html b/decide/authentication/templates/authentication/password_reset.html index fd7813151..d34471c45 100644 --- a/decide/authentication/templates/authentication/password_reset.html +++ b/decide/authentication/templates/authentication/password_reset.html @@ -8,7 +8,18 @@ {% endblock %} {% block content %} -
+ +

Forgot your password?

+

Enter your email address below, and we'll email instructions for setting a new one.

+ +
+ {% csrf_token %} + {{ form.as_p }} + +
+ + + + {% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_done.html b/decide/authentication/templates/authentication/password_reset_done.html index a041791b8..b5d4192b1 100644 --- a/decide/authentication/templates/authentication/password_reset_done.html +++ b/decide/authentication/templates/authentication/password_reset_done.html @@ -8,8 +8,12 @@ {% endblock %} {% block content %} -
+ +

Check your inbox.

+

We've emailed you instructions for setting your password. You should receive the email shortly!

+ + {% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 38a27c596..3a0c6e2f5 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -4,9 +4,11 @@ PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView, + PasswordResetView, ) from .views import ( + consulta_email, PasswordResetRequestView, GetUserView, LoginView, @@ -23,6 +25,7 @@ path("accounts/", include("allauth.urls")), path("register/", RegisterView.as_view(), name="register"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), + path("password_reset/", consulta_email, name="consulta_email1"), path("password_reset/", PasswordResetRequestView.as_view(), name="password_reset"), path( "password_reset/done/", diff --git a/decide/authentication/views.py b/decide/authentication/views.py index 25624e780..3ae762388 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -9,6 +9,20 @@ from .forms import LoginForm, RegisterForm from .serializers import UserSerializer +from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm +from django.urls import reverse_lazy +from django.views.generic.edit import FormView +from django.contrib.auth.views import ( + PasswordResetConfirmView as BasePasswordResetConfirmView, +) +from django.contrib.auth import update_session_auth_hash +from sendgrid import SendGridAPIClient +from sendgrid.helpers.mail import Mail +from dotenv import load_dotenv +import os +import base64 +from django.contrib.auth.models import User + # Non-api view class LoginView(TemplateView): @@ -79,3 +93,69 @@ def get(self, request): class ChangePasswordView(PasswordChangeView): template_name = "authentication/change_password.html" success_url = "/" + + +class PasswordResetRequestView(FormView): + template_name = "authentication/password_reset.html" + form_class = PasswordResetForm + success_url = reverse_lazy("/") # Replace 'home' with your home page URL name + + def post(self, request): + form = PasswordResetForm(request.POST) + + if form.is_valid(): + email = form.cleaned_data.get("email") + + user = User.objects.get(email=email) + + mailMessage = Mail( + from_email="decidezambrano@gmail.com", + to_emails=email, + ) + idEncode = f"salt{user.pk}" + encoded = base64.b64encode(bytes(idEncode, encoding="utf-8")).decode( + "utf-8" + ) + urlVerificar = f"{request.build_absolute_uri()}/{encoded}" + mailMessage.dynamic_template_data = { + "urlVerificar": urlVerificar, + "user": user.first_name, + } + mailMessage.template_id = "d-01c8e3b0691044009b4512599cf77eca" + load_dotenv() + print(os.getenv("SENDGRID_API_KEY")) + sg = SendGridAPIClient(os.getenv("SENDGRID_API_KEY")) + response = sg.send(mailMessage) + + def form_valid(self, form): + form.save( + use_https=self.request.is_secure(), + request=self.request, + ) + return super().form_valid(form) + + +def consulta_email(request, **kwargs): + encoded = kwargs.get("encoded", 0) + email = request.POST.get("email", None) + decode = base64.b64decode(str(encoded)).decode("utf-8") + userId = decode.replace("salt", "") + user = User.objects.get(pk=userId) + return render( + request, + "authentication/password_reset_confirm.html", + {"user": user, "hash": encoded}, + ) + + +class PasswordResetConfirmView(BasePasswordResetConfirmView): + template_name = "password_reset.html" + form_class = SetPasswordForm + success_url = reverse_lazy( + "/signin/" + ) # Replace 'login' with your login page URL name + + def form_valid(self, form): + user = form.save() + update_session_auth_hash(self.request, user) + return super().form_valid(form) diff --git a/decide/decide/settings.py b/decide/decide/settings.py index 6b697d5b0..db390550f 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -56,6 +56,7 @@ "allauth.socialaccount", "allauth.socialaccount.providers.google", "social_django", + "django_rest_passwordreset", ] SOCIALACCOUNT_PROVIDERS = { @@ -201,6 +202,11 @@ ALLOWED_VERSIONS = ["v1", "v2"] DEFAULT_VERSION = "v1" + +# Restore password +EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" +EMAIL_FILE_PATH = BASE_DIR + "/sent_emails" + try: from local_settings import * except ImportError: diff --git a/requirements.txt b/requirements.txt index bb0a98d4c..cef8764d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,53 @@ +asgiref==3.7.2 +attrs==23.1.0 +certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==2.1.1 +coreapi==2.3.3 +coreschema==0.0.4 +coverage==6.5.0 +cryptography==41.0.5 +defusedxml==0.7.1 Django==4.1 -pycryptodome==3.15.0 -djangorestframework==3.14.0 +django-allauth==0.58.2 django-cors-headers==3.13.0 -requests==2.28.1 django-filter==22.1 -psycopg2==2.9.4 -coverage==6.5.0 -jsonnet==0.18.0 django-nose==1.4.6 +django-rest-passwordreset==1.3.0 django-rest-swagger==2.2.0 -selenium==4.7.2 -django-allauth -python-dotenv +djangorestframework==3.14.0 +exceptiongroup==1.1.3 +h11==0.14.0 +idna==3.4 +itypes==1.2.0 +Jinja2==3.1.2 +jsonnet==0.18.0 +MarkupSafe==2.1.3 +nose==1.3.7 +oauthlib==3.2.2 +openapi-codec==1.3.2 +outcome==1.3.0.post0 +psycopg2==2.9.4 +pycparser==2.21 +pycryptodome==3.15.0 +PyJWT==2.8.0 pynose==1.4.8 -social-auth-app-django +PySocks==1.7.1 +python-dotenv==1.0.0 +python3-openid==3.2.0 +pytz==2023.3.post1 +requests==2.28.1 +requests-oauthlib==1.3.1 +selenium==4.7.2 +simplejson==3.19.2 +sniffio==1.3.0 +social-auth-app-django==5.4.0 +social-auth-core==4.5.1 +sortedcontainers==2.4.0 +sqlparse==0.4.4 +trio==0.23.0 +trio-websocket==0.11.1 +typing_extensions==4.8.0 +uritemplate==4.1.1 +urllib3==1.26.18 +wsproto==1.2.0 From 8b3d34bd47a32cf45765d1190fe9651d3a2490f4 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Thu, 14 Dec 2023 18:28:36 +0100 Subject: [PATCH 03/16] feat: trying husky --- .../password_reset_complete.html | 2 +- decide/authentication/urls.py | 21 ++-- decide/authentication/views.py | 96 ++++++++++--------- decide/decide/settings.py | 5 - requirements.txt | 20 ++-- 5 files changed, 66 insertions(+), 78 deletions(-) diff --git a/decide/authentication/templates/authentication/password_reset_complete.html b/decide/authentication/templates/authentication/password_reset_complete.html index 6bd10e62c..e30afad4c 100644 --- a/decide/authentication/templates/authentication/password_reset_complete.html +++ b/decide/authentication/templates/authentication/password_reset_complete.html @@ -10,6 +10,6 @@ {% block content %}

Restaurar Contraseña

-

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

+

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

{% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 3a0c6e2f5..97114d90c 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -1,20 +1,16 @@ from django.urls import path, include from rest_framework.authtoken.views import obtain_auth_token -from django.contrib.auth.views import ( - PasswordResetDoneView, - PasswordResetConfirmView, - PasswordResetCompleteView, - PasswordResetView, -) from .views import ( - consulta_email, - PasswordResetRequestView, GetUserView, LoginView, LogoutView, RegisterView, ChangePasswordView, + ResetPasswordView, + ResetPasswordDoneView, + ResetPasswordConfirmView, + ResetPasswordCompleteView, ) urlpatterns = [ @@ -25,21 +21,20 @@ path("accounts/", include("allauth.urls")), path("register/", RegisterView.as_view(), name="register"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), - path("password_reset/", consulta_email, name="consulta_email1"), - path("password_reset/", PasswordResetRequestView.as_view(), name="password_reset"), + path("password_reset/", ResetPasswordView.as_view(), name="password_reset"), path( "password_reset/done/", - PasswordResetDoneView.as_view(), + ResetPasswordDoneView.as_view(), name="password_reset_done", ), path( "reset///", - PasswordResetConfirmView.as_view(), + ResetPasswordConfirmView.as_view(), name="password_reset_confirm", ), path( "reset/done/", - PasswordResetCompleteView.as_view(), + ResetPasswordCompleteView.as_view(), name="password_reset_complete", ), path("social-auth/", include("social_django.urls", namespace="social_auth")), diff --git a/decide/authentication/views.py b/decide/authentication/views.py index 3ae762388..fc148bd09 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -1,3 +1,5 @@ +import os +import base64 from django.contrib.auth import authenticate, login, logout from django.contrib.auth.views import PasswordChangeView from django.shortcuts import get_object_or_404, redirect, render @@ -13,15 +15,17 @@ from django.urls import reverse_lazy from django.views.generic.edit import FormView from django.contrib.auth.views import ( - PasswordResetConfirmView as BasePasswordResetConfirmView, + PasswordResetView, + PasswordResetDoneView, + PasswordResetCompleteView, + PasswordResetConfirmView, ) from django.contrib.auth import update_session_auth_hash from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from dotenv import load_dotenv -import os -import base64 from django.contrib.auth.models import User +from django.contrib.sites.models import Site # Non-api view @@ -95,67 +99,65 @@ class ChangePasswordView(PasswordChangeView): success_url = "/" -class PasswordResetRequestView(FormView): +class ResetPasswordView(PasswordResetView): template_name = "authentication/password_reset.html" - form_class = PasswordResetForm - success_url = reverse_lazy("/") # Replace 'home' with your home page URL name + success_url = "/password_reset/done/" def post(self, request): form = PasswordResetForm(request.POST) if form.is_valid(): email = form.cleaned_data.get("email") - - user = User.objects.get(email=email) - mailMessage = Mail( from_email="decidezambrano@gmail.com", to_emails=email, ) + + user = User.objects.get(email=email) + token = form.cleaned_data.get("csrf_token") + idEncode = f"salt{user.pk}" encoded = base64.b64encode(bytes(idEncode, encoding="utf-8")).decode( "utf-8" ) - urlVerificar = f"{request.build_absolute_uri()}/{encoded}" + urlVerificar = ( + f"{Site.objects.get_current().domain}/reset/{encoded}//" + ) + mailMessage.dynamic_template_data = { "urlVerificar": urlVerificar, "user": user.first_name, } mailMessage.template_id = "d-01c8e3b0691044009b4512599cf77eca" - load_dotenv() - print(os.getenv("SENDGRID_API_KEY")) - sg = SendGridAPIClient(os.getenv("SENDGRID_API_KEY")) - response = sg.send(mailMessage) - - def form_valid(self, form): - form.save( - use_https=self.request.is_secure(), - request=self.request, - ) - return super().form_valid(form) - - -def consulta_email(request, **kwargs): - encoded = kwargs.get("encoded", 0) - email = request.POST.get("email", None) - decode = base64.b64decode(str(encoded)).decode("utf-8") - userId = decode.replace("salt", "") - user = User.objects.get(pk=userId) - return render( - request, - "authentication/password_reset_confirm.html", - {"user": user, "hash": encoded}, - ) - - -class PasswordResetConfirmView(BasePasswordResetConfirmView): - template_name = "password_reset.html" - form_class = SetPasswordForm - success_url = reverse_lazy( - "/signin/" - ) # Replace 'login' with your login page URL name - - def form_valid(self, form): - user = form.save() - update_session_auth_hash(self.request, user) - return super().form_valid(form) + try: + sg = SendGridAPIClient(os.environ.get("SENDGRID_API_KEY")) + sg.send(mailMessage) + except Exception as e: + print(e) + + return render( + request, "authentication/password_reset_done.html", {"form": form} + ) + + else: + msg = "No user with that email" + return render( + request, + "authentication/password_reset.html", + {"form": form, "message": msg}, + ) + + +class ResetPasswordDoneView(PasswordResetDoneView): + template_name = "authentication/password_reset_done.html" + success_url = "/" + + +class ResetPasswordConfirmView(PasswordResetConfirmView): + template_name = "authentication/password_reset_confirm.html" + success_url = "/reset/done/" + + +class ResetPasswordCompleteView(PasswordResetCompleteView): + template_name = "authentication/password_reset_complete.html" + success_url = "/" diff --git a/decide/decide/settings.py b/decide/decide/settings.py index fe418891c..0e16c6712 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -196,11 +196,6 @@ STATIC_URL = "/static/" -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "static"), -] - - # number of bits for the key, all auths should use the same number of bits KEYBITS = 256 diff --git a/requirements.txt b/requirements.txt index d41c45a36..6465d6476 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,25 +5,21 @@ cffi==1.16.0 charset-normalizer==2.1.1 coreapi==2.3.3 coreschema==0.0.4 -Django==4.1 -pycryptodome==3.15.0 -djangorestframework==3.14.0 -django-cors-headers==3.13.0 -requests==2.28.1 -django-filter==22.1 -psycopg2==2.9.4 -psycopg2-binary==2.9.4 coverage==6.5.0 cryptography==41.0.5 defusedxml==0.7.1 +dj-database-url==2.1.0 +Django==4.1 django-allauth==0.58.2 django-cors-headers==3.13.0 django-filter==22.1 django-nose==1.4.6 +django-recaptcha==4.0.0 django-rest-passwordreset==1.3.0 django-rest-swagger==2.2.0 djangorestframework==3.14.0 exceptiongroup==1.1.3 +gunicorn==21.2.0 h11==0.14.0 idna==3.4 itypes==1.2.0 @@ -34,7 +30,9 @@ nose==1.3.7 oauthlib==3.2.2 openapi-codec==1.3.2 outcome==1.3.0.post0 +packaging==23.2 psycopg2==2.9.4 +psycopg2-binary==2.9.4 pycparser==2.21 pycryptodome==3.15.0 PyJWT==2.8.0 @@ -57,8 +55,6 @@ trio-websocket==0.11.1 typing_extensions==4.8.0 uritemplate==4.1.1 urllib3==1.26.18 -wsproto==1.2.0 -dj-database-url == 2.1.0 whitenoise==6.5.0 -gunicorn==21.2.0 -social-auth-app-django +wsproto==1.2.0 +sendgrid \ No newline at end of file From 0991def3a15bde71f2eb9520c48a1824ee9c4656 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Thu, 14 Dec 2023 18:32:14 +0100 Subject: [PATCH 04/16] feat: sending email to user works --- .../password_reset_complete.html | 2 +- decide/authentication/urls.py | 21 ++-- decide/authentication/views.py | 96 ++++++++++--------- decide/decide/settings.py | 5 - requirements.txt | 20 ++-- 5 files changed, 66 insertions(+), 78 deletions(-) diff --git a/decide/authentication/templates/authentication/password_reset_complete.html b/decide/authentication/templates/authentication/password_reset_complete.html index 6bd10e62c..e30afad4c 100644 --- a/decide/authentication/templates/authentication/password_reset_complete.html +++ b/decide/authentication/templates/authentication/password_reset_complete.html @@ -10,6 +10,6 @@ {% block content %}

Restaurar Contraseña

-

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

+

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

{% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 3a0c6e2f5..97114d90c 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -1,20 +1,16 @@ from django.urls import path, include from rest_framework.authtoken.views import obtain_auth_token -from django.contrib.auth.views import ( - PasswordResetDoneView, - PasswordResetConfirmView, - PasswordResetCompleteView, - PasswordResetView, -) from .views import ( - consulta_email, - PasswordResetRequestView, GetUserView, LoginView, LogoutView, RegisterView, ChangePasswordView, + ResetPasswordView, + ResetPasswordDoneView, + ResetPasswordConfirmView, + ResetPasswordCompleteView, ) urlpatterns = [ @@ -25,21 +21,20 @@ path("accounts/", include("allauth.urls")), path("register/", RegisterView.as_view(), name="register"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), - path("password_reset/", consulta_email, name="consulta_email1"), - path("password_reset/", PasswordResetRequestView.as_view(), name="password_reset"), + path("password_reset/", ResetPasswordView.as_view(), name="password_reset"), path( "password_reset/done/", - PasswordResetDoneView.as_view(), + ResetPasswordDoneView.as_view(), name="password_reset_done", ), path( "reset///", - PasswordResetConfirmView.as_view(), + ResetPasswordConfirmView.as_view(), name="password_reset_confirm", ), path( "reset/done/", - PasswordResetCompleteView.as_view(), + ResetPasswordCompleteView.as_view(), name="password_reset_complete", ), path("social-auth/", include("social_django.urls", namespace="social_auth")), diff --git a/decide/authentication/views.py b/decide/authentication/views.py index 3ae762388..fc148bd09 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -1,3 +1,5 @@ +import os +import base64 from django.contrib.auth import authenticate, login, logout from django.contrib.auth.views import PasswordChangeView from django.shortcuts import get_object_or_404, redirect, render @@ -13,15 +15,17 @@ from django.urls import reverse_lazy from django.views.generic.edit import FormView from django.contrib.auth.views import ( - PasswordResetConfirmView as BasePasswordResetConfirmView, + PasswordResetView, + PasswordResetDoneView, + PasswordResetCompleteView, + PasswordResetConfirmView, ) from django.contrib.auth import update_session_auth_hash from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from dotenv import load_dotenv -import os -import base64 from django.contrib.auth.models import User +from django.contrib.sites.models import Site # Non-api view @@ -95,67 +99,65 @@ class ChangePasswordView(PasswordChangeView): success_url = "/" -class PasswordResetRequestView(FormView): +class ResetPasswordView(PasswordResetView): template_name = "authentication/password_reset.html" - form_class = PasswordResetForm - success_url = reverse_lazy("/") # Replace 'home' with your home page URL name + success_url = "/password_reset/done/" def post(self, request): form = PasswordResetForm(request.POST) if form.is_valid(): email = form.cleaned_data.get("email") - - user = User.objects.get(email=email) - mailMessage = Mail( from_email="decidezambrano@gmail.com", to_emails=email, ) + + user = User.objects.get(email=email) + token = form.cleaned_data.get("csrf_token") + idEncode = f"salt{user.pk}" encoded = base64.b64encode(bytes(idEncode, encoding="utf-8")).decode( "utf-8" ) - urlVerificar = f"{request.build_absolute_uri()}/{encoded}" + urlVerificar = ( + f"{Site.objects.get_current().domain}/reset/{encoded}//" + ) + mailMessage.dynamic_template_data = { "urlVerificar": urlVerificar, "user": user.first_name, } mailMessage.template_id = "d-01c8e3b0691044009b4512599cf77eca" - load_dotenv() - print(os.getenv("SENDGRID_API_KEY")) - sg = SendGridAPIClient(os.getenv("SENDGRID_API_KEY")) - response = sg.send(mailMessage) - - def form_valid(self, form): - form.save( - use_https=self.request.is_secure(), - request=self.request, - ) - return super().form_valid(form) - - -def consulta_email(request, **kwargs): - encoded = kwargs.get("encoded", 0) - email = request.POST.get("email", None) - decode = base64.b64decode(str(encoded)).decode("utf-8") - userId = decode.replace("salt", "") - user = User.objects.get(pk=userId) - return render( - request, - "authentication/password_reset_confirm.html", - {"user": user, "hash": encoded}, - ) - - -class PasswordResetConfirmView(BasePasswordResetConfirmView): - template_name = "password_reset.html" - form_class = SetPasswordForm - success_url = reverse_lazy( - "/signin/" - ) # Replace 'login' with your login page URL name - - def form_valid(self, form): - user = form.save() - update_session_auth_hash(self.request, user) - return super().form_valid(form) + try: + sg = SendGridAPIClient(os.environ.get("SENDGRID_API_KEY")) + sg.send(mailMessage) + except Exception as e: + print(e) + + return render( + request, "authentication/password_reset_done.html", {"form": form} + ) + + else: + msg = "No user with that email" + return render( + request, + "authentication/password_reset.html", + {"form": form, "message": msg}, + ) + + +class ResetPasswordDoneView(PasswordResetDoneView): + template_name = "authentication/password_reset_done.html" + success_url = "/" + + +class ResetPasswordConfirmView(PasswordResetConfirmView): + template_name = "authentication/password_reset_confirm.html" + success_url = "/reset/done/" + + +class ResetPasswordCompleteView(PasswordResetCompleteView): + template_name = "authentication/password_reset_complete.html" + success_url = "/" diff --git a/decide/decide/settings.py b/decide/decide/settings.py index fe418891c..0e16c6712 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -196,11 +196,6 @@ STATIC_URL = "/static/" -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "static"), -] - - # number of bits for the key, all auths should use the same number of bits KEYBITS = 256 diff --git a/requirements.txt b/requirements.txt index d41c45a36..6465d6476 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,25 +5,21 @@ cffi==1.16.0 charset-normalizer==2.1.1 coreapi==2.3.3 coreschema==0.0.4 -Django==4.1 -pycryptodome==3.15.0 -djangorestframework==3.14.0 -django-cors-headers==3.13.0 -requests==2.28.1 -django-filter==22.1 -psycopg2==2.9.4 -psycopg2-binary==2.9.4 coverage==6.5.0 cryptography==41.0.5 defusedxml==0.7.1 +dj-database-url==2.1.0 +Django==4.1 django-allauth==0.58.2 django-cors-headers==3.13.0 django-filter==22.1 django-nose==1.4.6 +django-recaptcha==4.0.0 django-rest-passwordreset==1.3.0 django-rest-swagger==2.2.0 djangorestframework==3.14.0 exceptiongroup==1.1.3 +gunicorn==21.2.0 h11==0.14.0 idna==3.4 itypes==1.2.0 @@ -34,7 +30,9 @@ nose==1.3.7 oauthlib==3.2.2 openapi-codec==1.3.2 outcome==1.3.0.post0 +packaging==23.2 psycopg2==2.9.4 +psycopg2-binary==2.9.4 pycparser==2.21 pycryptodome==3.15.0 PyJWT==2.8.0 @@ -57,8 +55,6 @@ trio-websocket==0.11.1 typing_extensions==4.8.0 uritemplate==4.1.1 urllib3==1.26.18 -wsproto==1.2.0 -dj-database-url == 2.1.0 whitenoise==6.5.0 -gunicorn==21.2.0 -social-auth-app-django +wsproto==1.2.0 +sendgrid \ No newline at end of file From bf5fc6f66ba13d9feaa828d8733a5ba3f26b4198 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Thu, 14 Dec 2023 19:55:23 +0100 Subject: [PATCH 05/16] feat: reset password confirm --- .../authentication/password_reset.html | 15 ++------- .../password_reset_confirm.html | 6 ++-- .../authentication/password_reset_done.html | 10 ++---- decide/authentication/views.py | 33 +++++++++++++++---- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/decide/authentication/templates/authentication/password_reset.html b/decide/authentication/templates/authentication/password_reset.html index d34471c45..0c5afdc93 100644 --- a/decide/authentication/templates/authentication/password_reset.html +++ b/decide/authentication/templates/authentication/password_reset.html @@ -8,18 +8,7 @@ {% endblock %} {% block content %} - -

Forgot your password?

-

Enter your email address below, and we'll email instructions for setting a new one.

- -
- {% csrf_token %} - {{ form.as_p }} - -
- - - +
{% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_confirm.html b/decide/authentication/templates/authentication/password_reset_confirm.html index f7dbdab31..01574e179 100644 --- a/decide/authentication/templates/authentication/password_reset_confirm.html +++ b/decide/authentication/templates/authentication/password_reset_confirm.html @@ -10,14 +10,12 @@ {% block content %}

Restaurar Contraseña

- {% if form.errors %} - {% for field, errors in form.errors.items %} + {% if message %}

- {{ errors|striptags }} + {{ message }}

- {% endfor %} {% endif %}
diff --git a/decide/authentication/templates/authentication/password_reset_done.html b/decide/authentication/templates/authentication/password_reset_done.html index b5d4192b1..8ea203fb4 100644 --- a/decide/authentication/templates/authentication/password_reset_done.html +++ b/decide/authentication/templates/authentication/password_reset_done.html @@ -8,12 +8,8 @@ {% endblock %} {% block content %} - -

Check your inbox.

-

We've emailed you instructions for setting your password. You should receive the email shortly!

- - +

Se ha enviado un correo a tu email para resetear la contraseña. Revisa tu correo y sigue las instrucciones.

+
{% endblock %} diff --git a/decide/authentication/views.py b/decide/authentication/views.py index fc148bd09..c8d850853 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -12,20 +12,17 @@ from .serializers import UserSerializer from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm -from django.urls import reverse_lazy -from django.views.generic.edit import FormView from django.contrib.auth.views import ( PasswordResetView, PasswordResetDoneView, PasswordResetCompleteView, PasswordResetConfirmView, ) -from django.contrib.auth import update_session_auth_hash from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail -from dotenv import load_dotenv from django.contrib.auth.models import User from django.contrib.sites.models import Site +from django.contrib.auth.tokens import PasswordResetTokenGenerator # Non-api view @@ -114,14 +111,14 @@ def post(self, request): ) user = User.objects.get(email=email) - token = form.cleaned_data.get("csrf_token") + token = PasswordResetTokenGenerator().make_token(user) idEncode = f"salt{user.pk}" encoded = base64.b64encode(bytes(idEncode, encoding="utf-8")).decode( "utf-8" ) urlVerificar = ( - f"{Site.objects.get_current().domain}/reset/{encoded}//" + f"{Site.objects.get_current().domain}/reset/{encoded}/{token}/" ) mailMessage.dynamic_template_data = { @@ -157,6 +154,30 @@ class ResetPasswordConfirmView(PasswordResetConfirmView): template_name = "authentication/password_reset_confirm.html" success_url = "/reset/done/" + def post(self, request): + form = SetPasswordForm(request.POST) + + new_password = request.POST.get("new_password1", None) + confirm_new_password = request.POST.get("new_password2", None) + print(new_password) + print(confirm_new_password) + if new_password: + if new_password == confirm_new_password: + request.user.set_password(new_password) + request.user.save() + return render( + request, + "authentication/password_reset_complete.html", + {"form": form}, + ) + else: + msg = "Las contraseñas no coinciden." + return render( + request, + "authentication/password_reset_complete.html", + {"form": form, "message": msg}, + ) + class ResetPasswordCompleteView(PasswordResetCompleteView): template_name = "authentication/password_reset_complete.html" From 1efcd6c484c7c2c44ecc1b607a6475f91c70c4ca Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Sun, 17 Dec 2023 18:56:21 +0100 Subject: [PATCH 06/16] feat: restore password working --- .../authentication/password_reset.html | 31 ++++--- .../password_reset_complete.html | 15 ++- .../password_reset_confirm.html | 73 +++++++++------ .../authentication/password_reset_done.html | 16 +++- decide/authentication/urls.py | 34 ++++--- decide/authentication/views.py | 91 +------------------ decide/decide/settings.py | 9 +- 7 files changed, 119 insertions(+), 150 deletions(-) diff --git a/decide/authentication/templates/authentication/password_reset.html b/decide/authentication/templates/authentication/password_reset.html index 0c5afdc93..29a902c77 100644 --- a/decide/authentication/templates/authentication/password_reset.html +++ b/decide/authentication/templates/authentication/password_reset.html @@ -4,31 +4,34 @@ {% block title %}Restore password{% endblock %} {% block extrahead %} - + {% endblock %} {% block content %}
-

Restaurar Contraseña

- {% if form.errors %} - {% for field, errors in form.errors.items %} -
-

- {{ errors|striptags }} -

-
- {% endfor %} - {% endif %} +
+
+

+ Recuperar Contraseña +

+ {% if form.email.errors %} +
+

+ {{ form.email.errors.0 }} +

+
+ {% endif %} +
{% csrf_token %}
- - + + {{ form.email }}
- +
diff --git a/decide/authentication/templates/authentication/password_reset_complete.html b/decide/authentication/templates/authentication/password_reset_complete.html index e30afad4c..f6da2dfb2 100644 --- a/decide/authentication/templates/authentication/password_reset_complete.html +++ b/decide/authentication/templates/authentication/password_reset_complete.html @@ -4,12 +4,21 @@ {% block title %}Restore password{% endblock %} {% block extrahead %} - + {% endblock %} {% block content %}
-

Restaurar Contraseña

-

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

+
+
+

+ Recuperar Contraseña +

+
+
+

Contraseña restarurada. Ahora puedes hacer acceder con tu nueva contraseña.

+
+ {% endblock %} + diff --git a/decide/authentication/templates/authentication/password_reset_confirm.html b/decide/authentication/templates/authentication/password_reset_confirm.html index 01574e179..78cc71dc4 100644 --- a/decide/authentication/templates/authentication/password_reset_confirm.html +++ b/decide/authentication/templates/authentication/password_reset_confirm.html @@ -4,35 +4,54 @@ {% block title %}Restore password{% endblock %} {% block extrahead %} - + {% endblock %} {% block content %} -
-

Restaurar Contraseña

- {% if message %} -
-

- {{ message }} -

-
- {% endif %} - -
- {% csrf_token %} -
-
- - -
-
- - -
-
-
- + {% if validlink %} +
+
+
+

+ Recuperar Contraseña +

+ {% if form.new_password1.errors %} +
+

+ {{ form.new_password1.errors.0 }} +

+
+ {% endif %} + {% if form.new_password2.errors %} +
+

+ {{ form.new_password2.errors.0 }} +

+
+ {% endif %}
- -
+
+ {% csrf_token %} +
+
+ + {{ form.new_password1 }} +
+
+ + {{ form.new_password2 }} +
+
+
+ +
+
+
+ + {% else %} +

+ The password reset link was invalid, possibly because it has already been used +

+ {% endif %} + {% endblock %} diff --git a/decide/authentication/templates/authentication/password_reset_done.html b/decide/authentication/templates/authentication/password_reset_done.html index 8ea203fb4..34d60fcfa 100644 --- a/decide/authentication/templates/authentication/password_reset_done.html +++ b/decide/authentication/templates/authentication/password_reset_done.html @@ -4,12 +4,20 @@ {% block title %}Restore password{% endblock %} {% block extrahead %} - + {% endblock %} {% block content %}
-

Restaurar Contraseña

-

Se ha enviado un correo a tu email para resetear la contraseña. Revisa tu correo y sigue las instrucciones.

-
+
+
+

+ Recuperar Contraseña +

+
+
+

Se ha enviado un correo a tu email para resetear la contraseña. Revisa tu correo y sigue las instrucciones.

+
+
+ {% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 97114d90c..2dd0f49ad 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -7,10 +7,12 @@ LogoutView, RegisterView, ChangePasswordView, - ResetPasswordView, - ResetPasswordDoneView, - ResetPasswordConfirmView, - ResetPasswordCompleteView, +) +from django.contrib.auth.views import ( + PasswordResetView, + PasswordResetDoneView, + PasswordResetCompleteView, + PasswordResetConfirmView, ) urlpatterns = [ @@ -21,20 +23,30 @@ path("accounts/", include("allauth.urls")), path("register/", RegisterView.as_view(), name="register"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), - path("password_reset/", ResetPasswordView.as_view(), name="password_reset"), path( - "password_reset/done/", - ResetPasswordDoneView.as_view(), + "password_reset/", + PasswordResetView.as_view(template_name="authentication/password_reset.html"), + name="password_reset", + ), + path( + "password_reset_done/", + PasswordResetDoneView.as_view( + template_name="authentication/password_reset_done.html" + ), name="password_reset_done", ), path( - "reset///", - ResetPasswordConfirmView.as_view(), + "password_reset_confirm///", + PasswordResetConfirmView.as_view( + template_name="authentication/password_reset_confirm.html" + ), name="password_reset_confirm", ), path( - "reset/done/", - ResetPasswordCompleteView.as_view(), + "password_reset_complete/", + PasswordResetCompleteView.as_view( + template_name="authentication/password_reset_complete.html" + ), name="password_reset_complete", ), path("social-auth/", include("social_django.urls", namespace="social_auth")), diff --git a/decide/authentication/views.py b/decide/authentication/views.py index c8d850853..0989342bc 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -11,18 +11,10 @@ from .forms import LoginForm, RegisterForm from .serializers import UserSerializer -from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm +from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.views import ( PasswordResetView, - PasswordResetDoneView, - PasswordResetCompleteView, - PasswordResetConfirmView, ) -from sendgrid import SendGridAPIClient -from sendgrid.helpers.mail import Mail -from django.contrib.auth.models import User -from django.contrib.sites.models import Site -from django.contrib.auth.tokens import PasswordResetTokenGenerator # Non-api view @@ -98,87 +90,8 @@ class ChangePasswordView(PasswordChangeView): class ResetPasswordView(PasswordResetView): template_name = "authentication/password_reset.html" - success_url = "/password_reset/done/" def post(self, request): form = PasswordResetForm(request.POST) - if form.is_valid(): - email = form.cleaned_data.get("email") - mailMessage = Mail( - from_email="decidezambrano@gmail.com", - to_emails=email, - ) - - user = User.objects.get(email=email) - token = PasswordResetTokenGenerator().make_token(user) - - idEncode = f"salt{user.pk}" - encoded = base64.b64encode(bytes(idEncode, encoding="utf-8")).decode( - "utf-8" - ) - urlVerificar = ( - f"{Site.objects.get_current().domain}/reset/{encoded}/{token}/" - ) - - mailMessage.dynamic_template_data = { - "urlVerificar": urlVerificar, - "user": user.first_name, - } - mailMessage.template_id = "d-01c8e3b0691044009b4512599cf77eca" - try: - sg = SendGridAPIClient(os.environ.get("SENDGRID_API_KEY")) - sg.send(mailMessage) - except Exception as e: - print(e) - - return render( - request, "authentication/password_reset_done.html", {"form": form} - ) - - else: - msg = "No user with that email" - return render( - request, - "authentication/password_reset.html", - {"form": form, "message": msg}, - ) - - -class ResetPasswordDoneView(PasswordResetDoneView): - template_name = "authentication/password_reset_done.html" - success_url = "/" - - -class ResetPasswordConfirmView(PasswordResetConfirmView): - template_name = "authentication/password_reset_confirm.html" - success_url = "/reset/done/" - - def post(self, request): - form = SetPasswordForm(request.POST) - - new_password = request.POST.get("new_password1", None) - confirm_new_password = request.POST.get("new_password2", None) - print(new_password) - print(confirm_new_password) - if new_password: - if new_password == confirm_new_password: - request.user.set_password(new_password) - request.user.save() - return render( - request, - "authentication/password_reset_complete.html", - {"form": form}, - ) - else: - msg = "Las contraseñas no coinciden." - return render( - request, - "authentication/password_reset_complete.html", - {"form": form, "message": msg}, - ) - - -class ResetPasswordCompleteView(PasswordResetCompleteView): - template_name = "authentication/password_reset_complete.html" - success_url = "/" + ## TODO: Check that email is in database before sending to user diff --git a/decide/decide/settings.py b/decide/decide/settings.py index 0e16c6712..0fbe6eff0 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -205,8 +205,13 @@ # Restore password -EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" -EMAIL_FILE_PATH = BASE_DIR + "/sent_emails" +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST = "smtp.gmail.com" +EMAIL_PORT = 587 +EMAIL_USE_TLS = True +EMAIL_HOST_USER = "decidezambrano@gmail.com" +EMAIL_HOST_PASSWORD = "yhgl cbmn baaj ppzs" + try: from local_settings import * From 680cd49d55675851ad1c9540cac34613a6cc72b8 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Mon, 18 Dec 2023 14:10:41 +0100 Subject: [PATCH 07/16] feat: check email in database --- .../authentication/password_reset.html | 7 +++++++ .../authentication/password_reset_confirm.html | 10 +++++++--- decide/authentication/urls.py | 4 ++-- decide/authentication/views.py | 18 +++++++++++------- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/decide/authentication/templates/authentication/password_reset.html b/decide/authentication/templates/authentication/password_reset.html index 29a902c77..dc72b0902 100644 --- a/decide/authentication/templates/authentication/password_reset.html +++ b/decide/authentication/templates/authentication/password_reset.html @@ -21,6 +21,13 @@

{% endif %} + {% if messages %} +
+ {% for message in messages %} +

{{ message }}

+ {% endfor %} +
+ {% endif %}

{% csrf_token %} diff --git a/decide/authentication/templates/authentication/password_reset_confirm.html b/decide/authentication/templates/authentication/password_reset_confirm.html index 78cc71dc4..6888bcc30 100644 --- a/decide/authentication/templates/authentication/password_reset_confirm.html +++ b/decide/authentication/templates/authentication/password_reset_confirm.html @@ -49,9 +49,13 @@

{% else %} -

- The password reset link was invalid, possibly because it has already been used -

+
+
+

+ The password reset link was invalid, possibly because it has already been used +

+
+
{% endif %} {% endblock %} diff --git a/decide/authentication/urls.py b/decide/authentication/urls.py index 2dd0f49ad..379244366 100644 --- a/decide/authentication/urls.py +++ b/decide/authentication/urls.py @@ -7,9 +7,9 @@ LogoutView, RegisterView, ChangePasswordView, + ResetPasswordView, ) from django.contrib.auth.views import ( - PasswordResetView, PasswordResetDoneView, PasswordResetCompleteView, PasswordResetConfirmView, @@ -25,7 +25,7 @@ path("change-password/", ChangePasswordView.as_view(), name="change-password"), path( "password_reset/", - PasswordResetView.as_view(template_name="authentication/password_reset.html"), + ResetPasswordView.as_view(template_name="authentication/password_reset.html"), name="password_reset", ), path( diff --git a/decide/authentication/views.py b/decide/authentication/views.py index 0989342bc..1a1fbc1da 100644 --- a/decide/authentication/views.py +++ b/decide/authentication/views.py @@ -11,10 +11,11 @@ from .forms import LoginForm, RegisterForm from .serializers import UserSerializer -from django.contrib.auth.forms import PasswordResetForm +from django.contrib.auth.models import User from django.contrib.auth.views import ( PasswordResetView, ) +from django.contrib import messages # Non-api view @@ -89,9 +90,12 @@ class ChangePasswordView(PasswordChangeView): class ResetPasswordView(PasswordResetView): - template_name = "authentication/password_reset.html" - - def post(self, request): - form = PasswordResetForm(request.POST) - - ## TODO: Check that email is in database before sending to user + def form_valid(self, form): + email = form.cleaned_data.get("email") + if User.objects.filter(email=email).exists(): + return super().form_valid(form) + else: + messages.error(self.request, "Este email no esta registrado") + return render( + self.request, "authentication/password_reset.html", {"form": form} + ) From 0942b5fa182e1900d169cf43d7d046139002a4c4 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Mon, 18 Dec 2023 14:13:40 +0100 Subject: [PATCH 08/16] fix: hide email information --- decide/decide/settings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/decide/decide/settings.py b/decide/decide/settings.py index 0fbe6eff0..89910f29a 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -209,8 +209,6 @@ EMAIL_HOST = "smtp.gmail.com" EMAIL_PORT = 587 EMAIL_USE_TLS = True -EMAIL_HOST_USER = "decidezambrano@gmail.com" -EMAIL_HOST_PASSWORD = "yhgl cbmn baaj ppzs" try: From baaeba30dde649f92bcb168e6739cf7f13a4964b Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Mon, 18 Dec 2023 14:22:57 +0100 Subject: [PATCH 09/16] fix: merge conflicts --- requirements.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index b781abef8..056971af4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,11 +12,8 @@ dj-database-url==2.1.0 Django==4.1 django-allauth==0.58.2 django-cors-headers==3.13.0 -<<<<<<< HEAD -======= django-recaptcha==4.0.0 requests==2.28.1 ->>>>>>> develop django-filter==22.1 django-nose==1.4.6 django-recaptcha==4.0.0 From cfb37d0e826ef98998abde5d3bb9deffabff846a Mon Sep 17 00:00:00 2001 From: auroranavas Date: Mon, 18 Dec 2023 20:02:53 +0100 Subject: [PATCH 10/16] test: unit and selenium for password reset --- decide/authentication/test-selenium.py | 43 +++++++++++++++++++++++--- decide/authentication/tests.py | 31 +++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/decide/authentication/test-selenium.py b/decide/authentication/test-selenium.py index 54ffd15cc..1e13e8215 100644 --- a/decide/authentication/test-selenium.py +++ b/decide/authentication/test-selenium.py @@ -44,7 +44,9 @@ def setUp(self): options.headless = True options.add_argument("--no-sandbox") self.driver = webdriver.Chrome(options=options) - self.user = User.objects.create_user(username="testuser", password="testpass") + self.user = User.objects.create_user( + username="testuser", password="testpass", email="test@email.com" + ) super().setUp() app = SocialApp.objects.create( @@ -68,6 +70,36 @@ def tearDown(self): self.base.tearDown() + def test_password_reset(self): + self.driver.get(f"{self.live_server_url}/signin") + + self.driver.find_element(By.LINK_TEXT, "Recuperar Contraseña").click() + + self.assertEqual( + self.driver.current_url, + f"{self.live_server_url}/password_reset/", + ) + self.driver.find_element(By.ID, "id_email").send_keys("test@email.com") + self.driver.find_element(By.CSS_SELECTOR, ".btn").click() + + self.assertEqual( + self.driver.current_url, + f"{self.live_server_url}/authentication/password_reset_done/", + ) + + def test_password_reset_unknown_email(self): + self.driver.get(f"{self.live_server_url}/signin") + + self.driver.find_element(By.LINK_TEXT, "Recuperar Contraseña").click() + + self.driver.find_element(By.ID, "id_email").send_keys("unknown@email.com") + self.driver.find_element(By.CSS_SELECTOR, ".btn").click() + + self.assertEqual( + self.driver.current_url, + f"{self.live_server_url}/password_reset/", + ) + def test_sucessful_login(self): self.driver.get(f"{self.live_server_url}/signin") @@ -115,6 +147,11 @@ def test_nocaptcha(self): ) +""" + +""" + + class LoginGoogleTestCase(StaticLiveServerTestCase): def setUp(self): self.base = BaseTestCase() @@ -147,10 +184,6 @@ def test_sucessful_login(self): self.driver.get(f"{self.live_server_url}/signin") self.driver.find_element(By.CSS_SELECTOR, ".google-button").click() - self.assertEqual( - self.driver.current_url, - f"{self.live_server_url}/authentication/accounts/google/login/", - ) class LoginGithubTestCase(StaticLiveServerTestCase): diff --git a/decide/authentication/tests.py b/decide/authentication/tests.py index 1b30aa843..719d4b8cb 100644 --- a/decide/authentication/tests.py +++ b/decide/authentication/tests.py @@ -6,6 +6,7 @@ from django.urls import reverse from unittest import mock from rest_framework.test import APIClient, APITestCase +from django.contrib.messages import get_messages from .forms import TestLoginForm, TestRegisterForm @@ -269,3 +270,33 @@ def test_change_password_view_failure(self): ) self.user.refresh_from_db() self.assertTrue(self.user.check_password("testpass")) + + +class ResetPasswordViewTestCase(TestCase): + def setUp(self): + self.user = User.objects.create_user( + username="passuser", password="testpass", email="test@example.com" + ) + self.url = reverse("password_reset") + + # Disable recaptcha + os.environ["DISABLE_RECAPTCHA"] = "1" + + def test_form_valid_with_existing_email(self): + email = self.user.email + data = {"email": email} + + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, reverse("password_reset_done")) + + def test_form_valid_with_non_existing_email(self): + data = {"email": "nonexisting@example.com"} + + response = self.client.post(self.url, data) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "authentication/password_reset.html") + messages = list(get_messages(response.wsgi_request)) + self.assertEqual(len(messages), 1) + self.assertEqual(str(messages[0]), "Este email no esta registrado") From 62f4e597a54f5d6da36f9dc706c67fb1b5b60613 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Mon, 18 Dec 2023 20:20:02 +0100 Subject: [PATCH 11/16] fix: secret keys changed to env variables for petition of the tester --- decide/decide/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/decide/decide/settings.py b/decide/decide/settings.py index a1d3e505e..cecd26e32 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -223,6 +223,8 @@ EMAIL_HOST = "smtp.gmail.com" EMAIL_PORT = 587 EMAIL_USE_TLS = True +EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "") +EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "") try: From d1f5b5ea257327d1acada982005bfb6ed6cc1e1c Mon Sep 17 00:00:00 2001 From: Aurora Navas <73229219+auroranavas@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:00:59 +0100 Subject: [PATCH 12/16] refactor: remove comments --- decide/authentication/test-selenium.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/decide/authentication/test-selenium.py b/decide/authentication/test-selenium.py index 1e13e8215..d94f2f1e9 100644 --- a/decide/authentication/test-selenium.py +++ b/decide/authentication/test-selenium.py @@ -146,12 +146,6 @@ def test_nocaptcha(self): "Error en el formulario", ) - -""" - -""" - - class LoginGoogleTestCase(StaticLiveServerTestCase): def setUp(self): self.base = BaseTestCase() From 2b1ed03510d63b741f026bffecdb977b472394fc Mon Sep 17 00:00:00 2001 From: auroranavas Date: Tue, 19 Dec 2023 11:01:27 +0000 Subject: [PATCH 13/16] refactor: Format code with Black --- decide/authentication/test-selenium.py | 1 + 1 file changed, 1 insertion(+) diff --git a/decide/authentication/test-selenium.py b/decide/authentication/test-selenium.py index d94f2f1e9..3e21d5563 100644 --- a/decide/authentication/test-selenium.py +++ b/decide/authentication/test-selenium.py @@ -146,6 +146,7 @@ def test_nocaptcha(self): "Error en el formulario", ) + class LoginGoogleTestCase(StaticLiveServerTestCase): def setUp(self): self.base = BaseTestCase() From c0b1428fa48469b0668bc1a804015bb5ade16744 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Tue, 19 Dec 2023 12:07:47 +0100 Subject: [PATCH 14/16] fix: reviewer petition lenguage conflict --- .../templates/authentication/password_reset_confirm.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decide/authentication/templates/authentication/password_reset_confirm.html b/decide/authentication/templates/authentication/password_reset_confirm.html index 6888bcc30..32390eede 100644 --- a/decide/authentication/templates/authentication/password_reset_confirm.html +++ b/decide/authentication/templates/authentication/password_reset_confirm.html @@ -52,7 +52,7 @@

- The password reset link was invalid, possibly because it has already been used + El link ya no está disponible, probablemete porque ya ha sido utilizado o ha expirado.

From 48124f2687c31b84e7fbfbc6ae8b17931634f250 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Tue, 19 Dec 2023 13:40:07 +0100 Subject: [PATCH 15/16] fix: remove unneccesary requirements --- requirements.txt | 62 ++++++++---------------------------------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/requirements.txt b/requirements.txt index 056971af4..5c9e25d38 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,64 +1,22 @@ -asgiref==3.7.2 -attrs==23.1.0 -certifi==2023.7.22 -cffi==1.16.0 -charset-normalizer==2.1.1 -coreapi==2.3.3 -coreschema==0.0.4 -coverage==6.5.0 -cryptography==41.0.5 -defusedxml==0.7.1 -dj-database-url==2.1.0 Django==4.1 -django-allauth==0.58.2 +pycryptodome==3.15.0 +djangorestframework==3.14.0 django-cors-headers==3.13.0 django-recaptcha==4.0.0 requests==2.28.1 django-filter==22.1 -django-nose==1.4.6 -django-recaptcha==4.0.0 -django-rest-passwordreset==1.3.0 -django-rest-swagger==2.2.0 -djangorestframework==3.14.0 -exceptiongroup==1.1.3 -gunicorn==21.2.0 -h11==0.14.0 -idna==3.4 -itypes==1.2.0 -Jinja2==3.1.2 -jsonnet==0.18.0 -MarkupSafe==2.1.3 -nose==1.3.7 -oauthlib==3.2.2 -openapi-codec==1.3.2 -outcome==1.3.0.post0 -packaging==23.2 psycopg2==2.9.4 psycopg2-binary==2.9.4 -pycparser==2.21 -pycryptodome==3.15.0 -PyJWT==2.8.0 -pynose==1.4.8 -PySocks==1.7.1 -python-dotenv==1.0.0 -python3-openid==3.2.0 -pytz==2023.3.post1 -requests==2.28.1 -requests-oauthlib==1.3.1 +coverage==6.5.0 +jsonnet==0.18.0 +django-nose==1.4.6 +django-rest-swagger==2.2.0 selenium==4.7.2 -simplejson==3.19.2 -sniffio==1.3.0 -social-auth-app-django==5.4.0 -social-auth-core==4.5.1 -sortedcontainers==2.4.0 -sqlparse==0.4.4 -trio==0.23.0 -trio-websocket==0.11.1 -typing_extensions==4.8.0 -uritemplate==4.1.1 -urllib3==1.26.18 +django-allauth +python-dotenv +pynose==1.4.8 +dj-database-url == 2.1.0 whitenoise==6.5.0 -wsproto==1.2.0 gunicorn==21.2.0 social-auth-app-django locust==2.20.0 From cbb46e229333c3f6a42d4eeb3e71803dc0699b31 Mon Sep 17 00:00:00 2001 From: marnunrey2 Date: Tue, 19 Dec 2023 14:05:39 +0100 Subject: [PATCH 16/16] fix: missed requirements --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5c9e25d38..da1513b7c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,5 @@ whitenoise==6.5.0 gunicorn==21.2.0 social-auth-app-django locust==2.20.0 -sendgrid \ No newline at end of file +sendgrid +django-rest-passwordreset==1.3.0 \ No newline at end of file