summaryrefslogtreecommitdiff
path: root/python-django-rest-resetpassword.spec
diff options
context:
space:
mode:
Diffstat (limited to 'python-django-rest-resetpassword.spec')
-rw-r--r--python-django-rest-resetpassword.spec1164
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/).
+
+![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 <Python_Bot@openeuler.org> - 0.1.2-1
+- Package Spec generated