%global _empty_manifest_terminate_build 0 Name: python-django-rest-resetpassword Version: 0.1.2 Release: 1 Summary: An extension of django rest framework, providing a configurable password reset strategy License: BSD License URL: https://github.com/joshuachinemezu/django-rest-resetpassword.git Source0: https://mirrors.nju.edu.cn/pypi/web/packages/6f/31/eb664a428ecc9bc4e8f0e99ee78a8c08fe315e0cbe582e31382a80bdc783/django_rest_resetpassword-0.1.2.tar.gz BuildArch: noarch %description # Django Rest Password Reset This python package provides a simple password reset strategy for django rest framework, where users can request password reset tokens via their registered e-mail address. Credits - https://github.com/anexia-it/django-rest-passwordreset The main idea behind this package is to not make any assumptions about how the token is delivered to the end-user (e-mail, text-message, etc...) and to major issues in the origin package. Also, this package provides a signal that can be reacted on (e.g., by sending an e-mail or a text message). This package basically provides two REST endpoints: - Request a token - Verify (confirm) a token (and change the password) ## Quickstart 1. Install the package from pypi using pip: ```bash pip install django-rest-resetpassword ``` 2. Add `django_rest_resetpassword` to your `INSTALLED_APPS` (after `rest_framework`) within your Django settings file: ```python INSTALLED_APPS = ( ... 'django.contrib.auth', ... 'rest_framework', ... 'django_rest_resetpassword', ... ) ``` 3. This package stores tokens in a separate database table (see [django_rest_resetpassword/models.py](django_rest_resetpassword/models.py)). Therefore, you have to run django migrations: ```bash python manage.py migrate ``` 4. This package provides two endpoints, which can be included by including `django_rest_resetpassword.urls` in your `urls.py` as follows: ```python from django.conf.urls import url, include urlpatterns = [ ... url(r'^accounts/password-reset/', include('django_rest_resetpassword.urls', namespace='password_reset')), ... ] ``` **Note**: You can adapt the url to your needs. ### Endpoints The following endpoints are provided: - `POST ${API_URL}/password-reset/` - request a reset password token by using the `email` parameter - `POST ${API_URL}//password-reset/validate_token/` - will return a 200 if a given `token` is valid - `POST ${API_URL}password-reset/confirm/` - using a valid `token`, the users password is set to the provided `password` where `${API_URL}/` is the url specified in your _urls.py_ (e.g., `api/password_reset/`) ### Signals - `reset_password_token_created(sender, instance, reset_password_token)` Fired when a reset password token is generated - `pre_password_reset(user)` - fired just before a password is being reset - `post_password_reset(user)` - fired after a password has been reset ### Example for sending an e-mail 1. Create two new django templates: `email/user_reset_password.html` and `email/user_reset_password.txt`. Those templates will contain the e-mail message sent to the user, aswell as the password reset link (or token). Within the templates, you can access the following context variables: `current_user`, `username`, `email`, `reset_password_url`. Feel free to adapt this to your needs. 2. Add the following code, which contains a Django Signal Receiver (`@receiver(...)`), to your application. Take care where to put this code, as it needs to be executed by the python interpreter (see the section _The `reset_password_token_created` signal is not fired_ below, aswell as [this part of the django documentation](https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-receiver-functions) and [How to Create Django Signals Tutorial](https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html) for more information). ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): """ Handles password reset tokens When a token is created, an e-mail needs to be sent to the user :param sender: View Class that sent the signal :param instance: View Instance that sent the signal :param reset_password_token: Token Model Object :param args: :param kwargs: :return: """ # send an e-mail to the user context = { 'current_user': reset_password_token.user, 'username': reset_password_token.user.username, 'email': reset_password_token.user.email, 'reset_password_url': "{}?token={}".format(reverse('password_reset:reset-password-request'), reset_password_token.key) } # render email text email_html_message = render_to_string('email/user_reset_password.html', context) email_plaintext_message = render_to_string('email/user_reset_password.txt', context) msg = EmailMultiAlternatives( # title: "Password Reset for {title}".format(title="Some website title"), # message: email_plaintext_message, # from: "noreply@somehost.local", # to: [reset_password_token.user.email] ) msg.attach_alternative(email_html_message, "text/html") msg.send() ``` 3. You should now be able to use the endpoints to request a password reset token via your e-mail address. If you want to test this locally, I recommend using some kind of fake mailserver (such as maildump). # Configuration / Settings The following settings can be set in Djangos `settings.py` file: - `DJANGO_REST_MULTITOKENAUTH_RESET_TOKEN_EXPIRY_TIME` - time in hours about how long the token is active (Default: 24) **Please note**: expired tokens are automatically cleared based on this setting in every call of `ResetPasswordRequestToken.post`. - `DJANGO_REST_RESETPASSWORD_NO_INFORMATION_LEAKAGE` - will cause a 200 to be returned on `POST ${API_URL}/reset_password/` even if the user doesn't exist in the databse (Default: False) - `DJANGO_REST_MULTITOKENAUTH_REQUIRE_USABLE_PASSWORD` - allows password reset for a user that does not [have a usable password](https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.has_usable_password) (Default: True) ## Custom Email Lookup By default, `email` lookup is used to find the user instance. You can change that by adding ```python DJANGO_REST_LOOKUP_FIELD = 'custom_email_field' ``` into Django settings.py file. ## Custom Remote IP Address and User Agent Header Lookup If your setup demands that the IP adress of the user is in another header (e.g., 'X-Forwarded-For'), you can configure that (using Django Request Headers): ```python DJANGO_REST_RESETPASSWORD_IP_ADDRESS_HEADER = 'HTTP_X_FORWARDED_FOR' ``` The same is true for the user agent: ```python HTTP_USER_AGENT_HEADER = 'HTTP_USER_AGENT' ``` ## Custom Token Generator By default, a random string token of length 10 to 50 is generated using the `RandomStringTokenGenerator` class. This library offers a possibility to configure the params of `RandomStringTokenGenerator` as well as switch to another token generator, e.g. `RandomNumberTokenGenerator`. You can also generate your own token generator class. You can change that by adding ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": ..., "OPTIONS": {...} } ``` into Django settings.py file. ### RandomStringTokenGenerator This is the default configuration. ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator" } ``` You can configure the length as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator", "OPTIONS": { "min_length": 20, "max_length": 30 } } ``` It uses `os.urandom()` to generate a good random string. ### RandomNumberTokenGenerator ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator" } ``` You can configure the minimum and maximum number as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator", "OPTIONS": { "min_number": 1500, "max_number": 9999 } } ``` It uses `random.SystemRandom().randint()` to generate a good random number. ### Write your own Token Generator Please see [token_configuration/django_rest_resetpassword/tokens.py](token_configuration/django_rest_resetpassword/tokens.py) for example implementation of number and string token generator. The basic idea is to create a new class that inherits from BaseTokenGenerator, takes arbitrary arguments (`args` and `kwargs`) in the `__init__` function as well as implementing a `generate_token` function. ```python from django_rest_resetpassword.tokens import BaseTokenGenerator class RandomStringTokenGenerator(BaseTokenGenerator): """ Generates a random string with min and max length using os.urandom and binascii.hexlify """ def __init__(self, min_length=10, max_length=50, *args, **kwargs): self.min_length = min_length self.max_length = max_length def generate_token(self, *args, **kwargs): """ generates a pseudo random code using os.urandom and binascii.hexlify """ # determine the length based on min_length and max_length length = random.randint(self.min_length, self.max_length) # generate the token using os.urandom and hexlify return binascii.hexlify( os.urandom(self.max_length) ).decode()[0:length] ``` ## Documentation / Browsable API This package supports the [DRF auto-generated documentation](https://www.django-rest-framework.org/topics/documenting-your-api/) (via `coreapi`) as well as the [DRF browsable API](https://www.django-rest-framework.org/topics/browsable-api/). ![drf_browsable_email_validation](docs/browsable_api_email_validation.png 'Browsable API E-Mail Validation') ![drf_browsable_password_validation](docs/browsable_api_password_validation.png 'Browsable API E-Mail Validation') ![coreapi_docs](docs/coreapi_docs.png 'Core API Docs') ## Known Issues / FAQ ### Django 2.1 Migrations - Multiple Primary keys for table ... Django 2.1 introduced a breaking change for migrations (see [Django Issue #29790](https://code.djangoproject.com/ticket/29790)). We therefore had to rewrite the migration [0002_pk_migration.py](django_rest_resetpassword/migrations/0002_pk_migration.py) such that it covers Django versions before (`<`) 2.1 and later (`>=`) 2.1. Some information is written down in Issue #8. ### The `reset_password_token_created` signal is not fired You need to make sure that the code with `@receiver(reset_password_token_created)` is executed by the python interpreter. To ensure this, you have two options: 1. Put the code at a place that is automatically loaded by Django (e.g., models.py, views.py), or 2. Import the file that contains the signal within your app.py `ready` function: _some_app/signals.py_ ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): # ... ``` _some_app/app.py_ ```python from django.apps import AppConfig class SomeAppConfig(AppConfig): name = 'your_django_project.some_app' verbose_name = 'Some App' def ready(self): import your_django_project.some_app.signals # noqa ``` _some_app/**init**.py_ ```python default_app_config = 'your_django_project.some_app.SomeAppConfig' ``` ### MongoDB not working Apparently, the following piece of code in the Django Model prevents MongodB from working: ```python id = models.AutoField( primary_key=True ) ``` See issue #49 for details. ## Contributions This library tries to follow the unix philosophy of "do one thing and do it well" (which is providing a basic password reset endpoint for Django Rest Framework). Contributions are welcome in the form of pull requests and issues! If you create a pull request, please make sure that you are not introducing breaking changes. ## Tests See folder [tests/](tests/). Basically, all endpoints are covered with multiple unit tests. Use this code snippet to run tests: ```bash pip install -r requirements_test.txt python setup.py install cd tests python manage.py test ``` ## Release on PyPi To release this package on pypi, the following steps are used: ```bash rm -rf dist/ build/ python setup.py sdist twine upload dist/* ``` %package -n python3-django-rest-resetpassword Summary: An extension of django rest framework, providing a configurable password reset strategy Provides: python-django-rest-resetpassword BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-django-rest-resetpassword # Django Rest Password Reset This python package provides a simple password reset strategy for django rest framework, where users can request password reset tokens via their registered e-mail address. Credits - https://github.com/anexia-it/django-rest-passwordreset The main idea behind this package is to not make any assumptions about how the token is delivered to the end-user (e-mail, text-message, etc...) and to major issues in the origin package. Also, this package provides a signal that can be reacted on (e.g., by sending an e-mail or a text message). This package basically provides two REST endpoints: - Request a token - Verify (confirm) a token (and change the password) ## Quickstart 1. Install the package from pypi using pip: ```bash pip install django-rest-resetpassword ``` 2. Add `django_rest_resetpassword` to your `INSTALLED_APPS` (after `rest_framework`) within your Django settings file: ```python INSTALLED_APPS = ( ... 'django.contrib.auth', ... 'rest_framework', ... 'django_rest_resetpassword', ... ) ``` 3. This package stores tokens in a separate database table (see [django_rest_resetpassword/models.py](django_rest_resetpassword/models.py)). Therefore, you have to run django migrations: ```bash python manage.py migrate ``` 4. This package provides two endpoints, which can be included by including `django_rest_resetpassword.urls` in your `urls.py` as follows: ```python from django.conf.urls import url, include urlpatterns = [ ... url(r'^accounts/password-reset/', include('django_rest_resetpassword.urls', namespace='password_reset')), ... ] ``` **Note**: You can adapt the url to your needs. ### Endpoints The following endpoints are provided: - `POST ${API_URL}/password-reset/` - request a reset password token by using the `email` parameter - `POST ${API_URL}//password-reset/validate_token/` - will return a 200 if a given `token` is valid - `POST ${API_URL}password-reset/confirm/` - using a valid `token`, the users password is set to the provided `password` where `${API_URL}/` is the url specified in your _urls.py_ (e.g., `api/password_reset/`) ### Signals - `reset_password_token_created(sender, instance, reset_password_token)` Fired when a reset password token is generated - `pre_password_reset(user)` - fired just before a password is being reset - `post_password_reset(user)` - fired after a password has been reset ### Example for sending an e-mail 1. Create two new django templates: `email/user_reset_password.html` and `email/user_reset_password.txt`. Those templates will contain the e-mail message sent to the user, aswell as the password reset link (or token). Within the templates, you can access the following context variables: `current_user`, `username`, `email`, `reset_password_url`. Feel free to adapt this to your needs. 2. Add the following code, which contains a Django Signal Receiver (`@receiver(...)`), to your application. Take care where to put this code, as it needs to be executed by the python interpreter (see the section _The `reset_password_token_created` signal is not fired_ below, aswell as [this part of the django documentation](https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-receiver-functions) and [How to Create Django Signals Tutorial](https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html) for more information). ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): """ Handles password reset tokens When a token is created, an e-mail needs to be sent to the user :param sender: View Class that sent the signal :param instance: View Instance that sent the signal :param reset_password_token: Token Model Object :param args: :param kwargs: :return: """ # send an e-mail to the user context = { 'current_user': reset_password_token.user, 'username': reset_password_token.user.username, 'email': reset_password_token.user.email, 'reset_password_url': "{}?token={}".format(reverse('password_reset:reset-password-request'), reset_password_token.key) } # render email text email_html_message = render_to_string('email/user_reset_password.html', context) email_plaintext_message = render_to_string('email/user_reset_password.txt', context) msg = EmailMultiAlternatives( # title: "Password Reset for {title}".format(title="Some website title"), # message: email_plaintext_message, # from: "noreply@somehost.local", # to: [reset_password_token.user.email] ) msg.attach_alternative(email_html_message, "text/html") msg.send() ``` 3. You should now be able to use the endpoints to request a password reset token via your e-mail address. If you want to test this locally, I recommend using some kind of fake mailserver (such as maildump). # Configuration / Settings The following settings can be set in Djangos `settings.py` file: - `DJANGO_REST_MULTITOKENAUTH_RESET_TOKEN_EXPIRY_TIME` - time in hours about how long the token is active (Default: 24) **Please note**: expired tokens are automatically cleared based on this setting in every call of `ResetPasswordRequestToken.post`. - `DJANGO_REST_RESETPASSWORD_NO_INFORMATION_LEAKAGE` - will cause a 200 to be returned on `POST ${API_URL}/reset_password/` even if the user doesn't exist in the databse (Default: False) - `DJANGO_REST_MULTITOKENAUTH_REQUIRE_USABLE_PASSWORD` - allows password reset for a user that does not [have a usable password](https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.has_usable_password) (Default: True) ## Custom Email Lookup By default, `email` lookup is used to find the user instance. You can change that by adding ```python DJANGO_REST_LOOKUP_FIELD = 'custom_email_field' ``` into Django settings.py file. ## Custom Remote IP Address and User Agent Header Lookup If your setup demands that the IP adress of the user is in another header (e.g., 'X-Forwarded-For'), you can configure that (using Django Request Headers): ```python DJANGO_REST_RESETPASSWORD_IP_ADDRESS_HEADER = 'HTTP_X_FORWARDED_FOR' ``` The same is true for the user agent: ```python HTTP_USER_AGENT_HEADER = 'HTTP_USER_AGENT' ``` ## Custom Token Generator By default, a random string token of length 10 to 50 is generated using the `RandomStringTokenGenerator` class. This library offers a possibility to configure the params of `RandomStringTokenGenerator` as well as switch to another token generator, e.g. `RandomNumberTokenGenerator`. You can also generate your own token generator class. You can change that by adding ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": ..., "OPTIONS": {...} } ``` into Django settings.py file. ### RandomStringTokenGenerator This is the default configuration. ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator" } ``` You can configure the length as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator", "OPTIONS": { "min_length": 20, "max_length": 30 } } ``` It uses `os.urandom()` to generate a good random string. ### RandomNumberTokenGenerator ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator" } ``` You can configure the minimum and maximum number as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator", "OPTIONS": { "min_number": 1500, "max_number": 9999 } } ``` It uses `random.SystemRandom().randint()` to generate a good random number. ### Write your own Token Generator Please see [token_configuration/django_rest_resetpassword/tokens.py](token_configuration/django_rest_resetpassword/tokens.py) for example implementation of number and string token generator. The basic idea is to create a new class that inherits from BaseTokenGenerator, takes arbitrary arguments (`args` and `kwargs`) in the `__init__` function as well as implementing a `generate_token` function. ```python from django_rest_resetpassword.tokens import BaseTokenGenerator class RandomStringTokenGenerator(BaseTokenGenerator): """ Generates a random string with min and max length using os.urandom and binascii.hexlify """ def __init__(self, min_length=10, max_length=50, *args, **kwargs): self.min_length = min_length self.max_length = max_length def generate_token(self, *args, **kwargs): """ generates a pseudo random code using os.urandom and binascii.hexlify """ # determine the length based on min_length and max_length length = random.randint(self.min_length, self.max_length) # generate the token using os.urandom and hexlify return binascii.hexlify( os.urandom(self.max_length) ).decode()[0:length] ``` ## Documentation / Browsable API This package supports the [DRF auto-generated documentation](https://www.django-rest-framework.org/topics/documenting-your-api/) (via `coreapi`) as well as the [DRF browsable API](https://www.django-rest-framework.org/topics/browsable-api/). ![drf_browsable_email_validation](docs/browsable_api_email_validation.png 'Browsable API E-Mail Validation') ![drf_browsable_password_validation](docs/browsable_api_password_validation.png 'Browsable API E-Mail Validation') ![coreapi_docs](docs/coreapi_docs.png 'Core API Docs') ## Known Issues / FAQ ### Django 2.1 Migrations - Multiple Primary keys for table ... Django 2.1 introduced a breaking change for migrations (see [Django Issue #29790](https://code.djangoproject.com/ticket/29790)). We therefore had to rewrite the migration [0002_pk_migration.py](django_rest_resetpassword/migrations/0002_pk_migration.py) such that it covers Django versions before (`<`) 2.1 and later (`>=`) 2.1. Some information is written down in Issue #8. ### The `reset_password_token_created` signal is not fired You need to make sure that the code with `@receiver(reset_password_token_created)` is executed by the python interpreter. To ensure this, you have two options: 1. Put the code at a place that is automatically loaded by Django (e.g., models.py, views.py), or 2. Import the file that contains the signal within your app.py `ready` function: _some_app/signals.py_ ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): # ... ``` _some_app/app.py_ ```python from django.apps import AppConfig class SomeAppConfig(AppConfig): name = 'your_django_project.some_app' verbose_name = 'Some App' def ready(self): import your_django_project.some_app.signals # noqa ``` _some_app/**init**.py_ ```python default_app_config = 'your_django_project.some_app.SomeAppConfig' ``` ### MongoDB not working Apparently, the following piece of code in the Django Model prevents MongodB from working: ```python id = models.AutoField( primary_key=True ) ``` See issue #49 for details. ## Contributions This library tries to follow the unix philosophy of "do one thing and do it well" (which is providing a basic password reset endpoint for Django Rest Framework). Contributions are welcome in the form of pull requests and issues! If you create a pull request, please make sure that you are not introducing breaking changes. ## Tests See folder [tests/](tests/). Basically, all endpoints are covered with multiple unit tests. Use this code snippet to run tests: ```bash pip install -r requirements_test.txt python setup.py install cd tests python manage.py test ``` ## Release on PyPi To release this package on pypi, the following steps are used: ```bash rm -rf dist/ build/ python setup.py sdist twine upload dist/* ``` %package help Summary: Development documents and examples for django-rest-resetpassword Provides: python3-django-rest-resetpassword-doc %description help # Django Rest Password Reset This python package provides a simple password reset strategy for django rest framework, where users can request password reset tokens via their registered e-mail address. Credits - https://github.com/anexia-it/django-rest-passwordreset The main idea behind this package is to not make any assumptions about how the token is delivered to the end-user (e-mail, text-message, etc...) and to major issues in the origin package. Also, this package provides a signal that can be reacted on (e.g., by sending an e-mail or a text message). This package basically provides two REST endpoints: - Request a token - Verify (confirm) a token (and change the password) ## Quickstart 1. Install the package from pypi using pip: ```bash pip install django-rest-resetpassword ``` 2. Add `django_rest_resetpassword` to your `INSTALLED_APPS` (after `rest_framework`) within your Django settings file: ```python INSTALLED_APPS = ( ... 'django.contrib.auth', ... 'rest_framework', ... 'django_rest_resetpassword', ... ) ``` 3. This package stores tokens in a separate database table (see [django_rest_resetpassword/models.py](django_rest_resetpassword/models.py)). Therefore, you have to run django migrations: ```bash python manage.py migrate ``` 4. This package provides two endpoints, which can be included by including `django_rest_resetpassword.urls` in your `urls.py` as follows: ```python from django.conf.urls import url, include urlpatterns = [ ... url(r'^accounts/password-reset/', include('django_rest_resetpassword.urls', namespace='password_reset')), ... ] ``` **Note**: You can adapt the url to your needs. ### Endpoints The following endpoints are provided: - `POST ${API_URL}/password-reset/` - request a reset password token by using the `email` parameter - `POST ${API_URL}//password-reset/validate_token/` - will return a 200 if a given `token` is valid - `POST ${API_URL}password-reset/confirm/` - using a valid `token`, the users password is set to the provided `password` where `${API_URL}/` is the url specified in your _urls.py_ (e.g., `api/password_reset/`) ### Signals - `reset_password_token_created(sender, instance, reset_password_token)` Fired when a reset password token is generated - `pre_password_reset(user)` - fired just before a password is being reset - `post_password_reset(user)` - fired after a password has been reset ### Example for sending an e-mail 1. Create two new django templates: `email/user_reset_password.html` and `email/user_reset_password.txt`. Those templates will contain the e-mail message sent to the user, aswell as the password reset link (or token). Within the templates, you can access the following context variables: `current_user`, `username`, `email`, `reset_password_url`. Feel free to adapt this to your needs. 2. Add the following code, which contains a Django Signal Receiver (`@receiver(...)`), to your application. Take care where to put this code, as it needs to be executed by the python interpreter (see the section _The `reset_password_token_created` signal is not fired_ below, aswell as [this part of the django documentation](https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-receiver-functions) and [How to Create Django Signals Tutorial](https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html) for more information). ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): """ Handles password reset tokens When a token is created, an e-mail needs to be sent to the user :param sender: View Class that sent the signal :param instance: View Instance that sent the signal :param reset_password_token: Token Model Object :param args: :param kwargs: :return: """ # send an e-mail to the user context = { 'current_user': reset_password_token.user, 'username': reset_password_token.user.username, 'email': reset_password_token.user.email, 'reset_password_url': "{}?token={}".format(reverse('password_reset:reset-password-request'), reset_password_token.key) } # render email text email_html_message = render_to_string('email/user_reset_password.html', context) email_plaintext_message = render_to_string('email/user_reset_password.txt', context) msg = EmailMultiAlternatives( # title: "Password Reset for {title}".format(title="Some website title"), # message: email_plaintext_message, # from: "noreply@somehost.local", # to: [reset_password_token.user.email] ) msg.attach_alternative(email_html_message, "text/html") msg.send() ``` 3. You should now be able to use the endpoints to request a password reset token via your e-mail address. If you want to test this locally, I recommend using some kind of fake mailserver (such as maildump). # Configuration / Settings The following settings can be set in Djangos `settings.py` file: - `DJANGO_REST_MULTITOKENAUTH_RESET_TOKEN_EXPIRY_TIME` - time in hours about how long the token is active (Default: 24) **Please note**: expired tokens are automatically cleared based on this setting in every call of `ResetPasswordRequestToken.post`. - `DJANGO_REST_RESETPASSWORD_NO_INFORMATION_LEAKAGE` - will cause a 200 to be returned on `POST ${API_URL}/reset_password/` even if the user doesn't exist in the databse (Default: False) - `DJANGO_REST_MULTITOKENAUTH_REQUIRE_USABLE_PASSWORD` - allows password reset for a user that does not [have a usable password](https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.has_usable_password) (Default: True) ## Custom Email Lookup By default, `email` lookup is used to find the user instance. You can change that by adding ```python DJANGO_REST_LOOKUP_FIELD = 'custom_email_field' ``` into Django settings.py file. ## Custom Remote IP Address and User Agent Header Lookup If your setup demands that the IP adress of the user is in another header (e.g., 'X-Forwarded-For'), you can configure that (using Django Request Headers): ```python DJANGO_REST_RESETPASSWORD_IP_ADDRESS_HEADER = 'HTTP_X_FORWARDED_FOR' ``` The same is true for the user agent: ```python HTTP_USER_AGENT_HEADER = 'HTTP_USER_AGENT' ``` ## Custom Token Generator By default, a random string token of length 10 to 50 is generated using the `RandomStringTokenGenerator` class. This library offers a possibility to configure the params of `RandomStringTokenGenerator` as well as switch to another token generator, e.g. `RandomNumberTokenGenerator`. You can also generate your own token generator class. You can change that by adding ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": ..., "OPTIONS": {...} } ``` into Django settings.py file. ### RandomStringTokenGenerator This is the default configuration. ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator" } ``` You can configure the length as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomStringTokenGenerator", "OPTIONS": { "min_length": 20, "max_length": 30 } } ``` It uses `os.urandom()` to generate a good random string. ### RandomNumberTokenGenerator ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator" } ``` You can configure the minimum and maximum number as follows: ```python DJANGO_REST_RESETPASSWORD_TOKEN_CONFIG = { "CLASS": "django_rest_resetpassword.tokens.RandomNumberTokenGenerator", "OPTIONS": { "min_number": 1500, "max_number": 9999 } } ``` It uses `random.SystemRandom().randint()` to generate a good random number. ### Write your own Token Generator Please see [token_configuration/django_rest_resetpassword/tokens.py](token_configuration/django_rest_resetpassword/tokens.py) for example implementation of number and string token generator. The basic idea is to create a new class that inherits from BaseTokenGenerator, takes arbitrary arguments (`args` and `kwargs`) in the `__init__` function as well as implementing a `generate_token` function. ```python from django_rest_resetpassword.tokens import BaseTokenGenerator class RandomStringTokenGenerator(BaseTokenGenerator): """ Generates a random string with min and max length using os.urandom and binascii.hexlify """ def __init__(self, min_length=10, max_length=50, *args, **kwargs): self.min_length = min_length self.max_length = max_length def generate_token(self, *args, **kwargs): """ generates a pseudo random code using os.urandom and binascii.hexlify """ # determine the length based on min_length and max_length length = random.randint(self.min_length, self.max_length) # generate the token using os.urandom and hexlify return binascii.hexlify( os.urandom(self.max_length) ).decode()[0:length] ``` ## Documentation / Browsable API This package supports the [DRF auto-generated documentation](https://www.django-rest-framework.org/topics/documenting-your-api/) (via `coreapi`) as well as the [DRF browsable API](https://www.django-rest-framework.org/topics/browsable-api/). ![drf_browsable_email_validation](docs/browsable_api_email_validation.png 'Browsable API E-Mail Validation') ![drf_browsable_password_validation](docs/browsable_api_password_validation.png 'Browsable API E-Mail Validation') ![coreapi_docs](docs/coreapi_docs.png 'Core API Docs') ## Known Issues / FAQ ### Django 2.1 Migrations - Multiple Primary keys for table ... Django 2.1 introduced a breaking change for migrations (see [Django Issue #29790](https://code.djangoproject.com/ticket/29790)). We therefore had to rewrite the migration [0002_pk_migration.py](django_rest_resetpassword/migrations/0002_pk_migration.py) such that it covers Django versions before (`<`) 2.1 and later (`>=`) 2.1. Some information is written down in Issue #8. ### The `reset_password_token_created` signal is not fired You need to make sure that the code with `@receiver(reset_password_token_created)` is executed by the python interpreter. To ensure this, you have two options: 1. Put the code at a place that is automatically loaded by Django (e.g., models.py, views.py), or 2. Import the file that contains the signal within your app.py `ready` function: _some_app/signals.py_ ```python from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.template.loader import render_to_string from django.urls import reverse from django_rest_resetpassword.signals import reset_password_token_created @receiver(reset_password_token_created) def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): # ... ``` _some_app/app.py_ ```python from django.apps import AppConfig class SomeAppConfig(AppConfig): name = 'your_django_project.some_app' verbose_name = 'Some App' def ready(self): import your_django_project.some_app.signals # noqa ``` _some_app/**init**.py_ ```python default_app_config = 'your_django_project.some_app.SomeAppConfig' ``` ### MongoDB not working Apparently, the following piece of code in the Django Model prevents MongodB from working: ```python id = models.AutoField( primary_key=True ) ``` See issue #49 for details. ## Contributions This library tries to follow the unix philosophy of "do one thing and do it well" (which is providing a basic password reset endpoint for Django Rest Framework). Contributions are welcome in the form of pull requests and issues! If you create a pull request, please make sure that you are not introducing breaking changes. ## Tests See folder [tests/](tests/). Basically, all endpoints are covered with multiple unit tests. Use this code snippet to run tests: ```bash pip install -r requirements_test.txt python setup.py install cd tests python manage.py test ``` ## Release on PyPi To release this package on pypi, the following steps are used: ```bash rm -rf dist/ build/ python setup.py sdist twine upload dist/* ``` %prep %autosetup -n django-rest-resetpassword-0.1.2 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-django-rest-resetpassword -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Mon May 15 2023 Python_Bot - 0.1.2-1 - Package Spec generated