From 6fdcbf4454a8880e5ccdf0922d4ab34f2d4eda93 Mon Sep 17 00:00:00 2001 From: chiahetcho <44137047+chiahetcho@users.noreply.github.com> Date: Thu, 12 Dec 2019 20:40:21 +0100 Subject: [PATCH] Password reset feature (#8) (#12) * Add Django Rest auth module * Try to make the send reset password email work * Modified template mail for reset * Add Django Rest auth module * Try to make the send reset password email work * Modified template mail for reset --- .gitignore | 1 + oser_backend/serializers.py | 34 ++++++++++++++++++++++++++++++ oser_backend/settings/common.py | 24 ++++++++++++++++++++- oser_backend/urls.py | 6 +++++- requirements.txt | 3 +++ templates/email-reset-template.txt | 15 +++++++++++++ 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 oser_backend/serializers.py create mode 100644 templates/email-reset-template.txt diff --git a/.gitignore b/.gitignore index 91b0f06..c4f4ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ static/ # Supervisor supervisord.pid +sendgrid.env diff --git a/oser_backend/serializers.py b/oser_backend/serializers.py new file mode 100644 index 0000000..576a486 --- /dev/null +++ b/oser_backend/serializers.py @@ -0,0 +1,34 @@ +from django.contrib.auth.forms import PasswordResetForm +from django.conf import settings +from django.utils.translation import gettext as _ +from rest_framework import serializers + +###### IMPORT YOUR USER MODEL ###### +from users.models import User + +class PasswordResetSerializer(serializers.Serializer): + email = serializers.EmailField() + password_reset_form_class = PasswordResetForm + def validate_email(self, value): + self.reset_form = self.password_reset_form_class(data=self.initial_data) + if not self.reset_form.is_valid(): + raise serializers.ValidationError(_('Error')) + + ###### FILTER YOUR USER MODEL ###### + if not User.objects.filter(email=value).exists(): + + raise serializers.ValidationError(_('Invalid e-mail address')) + return value + + def save(self): + request = self.context.get('request') + opts = { + 'use_https': request.is_secure(), + 'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'), + + ###### USE YOUR TEXT FILE ###### + 'email_template_name': 'email-reset-template.txt', + + 'request': request, + } + self.reset_form.save(**opts) \ No newline at end of file diff --git a/oser_backend/settings/common.py b/oser_backend/settings/common.py index 520d12c..ca0a607 100644 --- a/oser_backend/settings/common.py +++ b/oser_backend/settings/common.py @@ -60,6 +60,7 @@ THIRD_PARTY_APPS = [ 'django_countries', # Easy filtering on the API 'django_filters', + 'rest_auth', ] PROJECT_APPS = [ @@ -229,11 +230,32 @@ LOGGING = { }, } +# Connect custom PasswordResetSerializer to override default +REST_AUTH_SERIALIZERS = { + 'PASSWORD_RESET_SERIALIZER': + 'oser_backend.serializers.PasswordResetSerializer', +} + +DEFAULT_FROM_EMAIL = "admin@oser-cs.fr" # Email configuration EMAIL_BACKEND = 'sendgrid_backend.SendgridBackend' -SENDGRID_API_KEY = os.environ.get('SENDGRID_API_KEY') +SENDGRID_API_KEY = os.getenv('SENDGRID_API_KEY') + +# Sendgrid configuration + +EMAIL_HOST = 'smtp.sendgrid.net' +EMAIL_HOST_USER = 'apikey' +EMAIL_HOST_PASSWORD = SENDGRID_API_KEY +EMAIL_PORT = 587 +EMAIL_USE_TLS = True + +# Toggle sandbox mode (when running in DEBUG mode) +SENDGRID_SANDBOX_MODE_IN_DEBUG=False + +# echo to stdout or any other file-like object that is passed to the backend via the stream kwarg. +SENDGRID_ECHO_TO_STDOUT=True # Mails app config diff --git a/oser_backend/urls.py b/oser_backend/urls.py index 362d640..a645eb8 100644 --- a/oser_backend/urls.py +++ b/oser_backend/urls.py @@ -4,9 +4,10 @@ from django.conf import settings from django.conf.urls import url from django.conf.urls.static import static from django.contrib import admin -from django.urls import include +from django.urls import include, re_path from django.views.generic import RedirectView from rest_framework.documentation import include_docs_urls +from rest_auth.views import PasswordResetConfirmView urlpatterns = [ # Admin site @@ -19,10 +20,13 @@ urlpatterns = [ # DRF authentication routes url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')), + url(r'^api/rest-auth/', include('rest_auth.urls')), # API docs url(r'^api/docs/', include_docs_urls(title='OSER_CS API', public=False)), # Markdown 3rd party app url(r'^markdownx/', include('markdownx.urls')), + re_path(r'^rest-auth/password/reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', PasswordResetConfirmView.as_view(), + name='password_reset_confirm'), ] # Serve media files in development diff --git a/requirements.txt b/requirements.txt index d8d2179..924e008 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,8 @@ django-guardian # Permissions # Email via SendGrid django-sendgrid-v5 +django-filter +django-rest-auth # Storage of files in AWS S3 django-storages @@ -22,6 +24,7 @@ psycopg2 coreapi-cli # Required for automatic API docs django-cors-headers # CORS (security headers sent by browsers) django-filter # Filtering helpers for API endpoints +django-rest-auth # Password reset views dry_rest_permissions # Markdown rendering diff --git a/templates/email-reset-template.txt b/templates/email-reset-template.txt new file mode 100644 index 0000000..f222d59 --- /dev/null +++ b/templates/email-reset-template.txt @@ -0,0 +1,15 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}Vous recevez ce courriel car vous avez demander à +réinitialiser le mot de passe de votre compte Oser.{% endblocktrans %} + +{% trans "Please go to the following page and choose a new password:" %} +{% block reset_link %} +http://localhost:4200{% url 'password_reset_confirm' uidb64=uid token=token %} +{% endblock %} +{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The Oser team{% endblocktrans %} + +{% endautoescape %} \ No newline at end of file -- GitLab