diff --git a/.gitignore b/.gitignore index 91b0f06ecfac44499efb225d932d35aefd26255f..c4f4ed67f77c82d7555059e4e059483a4b04820e 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 0000000000000000000000000000000000000000..576a486df57cd73a4b7479200552c27528ddfb8a --- /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 520d12cb162e3e444675d552b64e83caf8c66687..ca0a6074c8124e6ee6b3a9c04c8e964b97c107e5 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 362d640839223f4e4166d9df55e8b9ea196bf436..a645eb8891e45df585c383ade65c77f98ee80fb9 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 d8d217989734e16e069b17b0ef2c6c230c8f1ef0..924e008527b9f4141c9146a3629b7428dd5767f6 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 0000000000000000000000000000000000000000..f222d5919241d2b60062eb0d6451bd619c690fb4 --- /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