diff options
author | CoprDistGit <infra@openeuler.org> | 2023-05-15 05:37:39 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-05-15 05:37:39 +0000 |
commit | 0b768b261d42b7c06d07a37c5ffff0fa5c1693a4 (patch) | |
tree | b0d9ed2ba6d7d240c7bac112c18a3d3b246bf8ab /python-django-rest-resetpassword.spec | |
parent | 58a1793615d0b2184d79f3f518f28754e3df4f6d (diff) |
automatic import of python-django-rest-resetpassword
Diffstat (limited to 'python-django-rest-resetpassword.spec')
-rw-r--r-- | python-django-rest-resetpassword.spec | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/python-django-rest-resetpassword.spec b/python-django-rest-resetpassword.spec new file mode 100644 index 0000000..611b676 --- /dev/null +++ b/python-django-rest-resetpassword.spec @@ -0,0 +1,1164 @@ +%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/). + + + + + + + +## 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/). + + + + + + + +## 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/). + + + + + + + +## 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 <Python_Bot@openeuler.org> - 0.1.2-1 +- Package Spec generated |