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