• Stars
    star
    416
  • Rank 104,068 (Top 3 %)
  • Language
    Python
  • License
    BSD 3-Clause "New...
  • Created over 7 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An extension of django rest framework, providing a configurable password reset strategy

Django Rest Password Reset

PyPI version build-and-test actions status Codecov

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.

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...). Instead, 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:
pip install django-rest-passwordreset
  1. Add django_rest_passwordreset to your INSTALLED_APPS (after rest_framework) within your Django settings file:
INSTALLED_APPS = (
    ...
    'django.contrib.auth',
    ...
    'rest_framework',
    ...
    'django_rest_passwordreset',
    ...
)
  1. This package stores tokens in a separate database table (see django_rest_passwordreset/models.py). Therefore, you have to run django migrations:
python manage.py migrate
  1. This package provides three endpoints, which can be included by including django_rest_passwordreset.urls in your urls.py as follows:
from django.urls import path, include

urlpatterns = [
    ...
    path(r'^api/password_reset/', include('django_rest_passwordreset.urls', namespace='password_reset')),
    ...
]

Note: You can adapt the URL to your needs.

Endpoints

The following endpoints are provided:

  • POST ${API_URL}/ - request a reset password token by using the email parameter
  • POST ${API_URL}/confirm/ - using a valid token, the users password is set to the provided password
  • POST ${API_URL}/validate_token/ - will return a 200 if a given token is valid

where ${API_URL}/ is the url specified in your urls.py (e.g., api/password_reset/ as in the example above)

Signals

  • reset_password_token_created(sender, instance, reset_password_token) Fired when a reset password token is generated
  • pre_password_reset(sender, user, reset_password_token) - fired just before a password is being reset
  • post_password_reset(sender, user, reset_password_token) - 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 and How to Create Django Signals Tutorial for more information).

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_passwordreset.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(
            instance.request.build_absolute_uri(reverse('password_reset:reset-password-confirm')),
            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:
        "[email protected]",
        # to:
        [reset_password_token.user.email]
    )
    msg.attach_alternative(email_html_message, "text/html")
    msg.send()
  1. 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 Django 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_PASSWORDRESET_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 (Default: True)

Custom Email Lookup

By default, email lookup is used to find the user instance. You can change that by adding

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):

DJANGO_REST_PASSWORDRESET_IP_ADDRESS_HEADER = 'HTTP_X_FORWARDED_FOR'

The same is true for the user agent:

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

DJANGO_REST_PASSWORDRESET_TOKEN_CONFIG = {
    "CLASS": ...,
    "OPTIONS": {...}
}

into Django settings.py file.

RandomStringTokenGenerator

This is the default configuration.

DJANGO_REST_PASSWORDRESET_TOKEN_CONFIG = {
    "CLASS": "django_rest_passwordreset.tokens.RandomStringTokenGenerator"
}

You can configure the length as follows:

DJANGO_REST_PASSWORDRESET_TOKEN_CONFIG = {
    "CLASS": "django_rest_passwordreset.tokens.RandomStringTokenGenerator",
    "OPTIONS": {
        "min_length": 20,
        "max_length": 30
    }
}

It uses os.urandom() to generate a good random string.

RandomNumberTokenGenerator

DJANGO_REST_PASSWORDRESET_TOKEN_CONFIG = {
    "CLASS": "django_rest_passwordreset.tokens.RandomNumberTokenGenerator"
}

You can configure the minimum and maximum number as follows:

DJANGO_REST_PASSWORDRESET_TOKEN_CONFIG = {
    "CLASS": "django_rest_passwordreset.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_passwordreset/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.

from django_rest_passwordreset.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]

Compatibility Matrix

This library should be compatible with the latest Django and Django Rest Framework Versions. For reference, here is a matrix showing the guaranteed and tested compatibility.

django-rest-passwordreset Version Django Versions Django Rest Framework Versions Python
0.9.7 1.8, 1.11, 2.0, 2.1 3.6 - 3.9 2.7
1.0 1.11, 2.0, 2.2 3.6 - 3.9 2.7
1.1 1.11, 2.2 3.6 - 3.9 2.7
1.2 2.2, 3.0, 3.1 3.10, 3.11 3.5 - 3.8
1.3 3.2, 4.0, 4.1 3.12, 3.13, 3.14 3.7 - 3.10

Documentation / Browsable API

This package supports the DRF auto-generated documentation (via coreapi) as well as the DRF browsable API.

To add the endpoints to the browsable API, you can use a helper function in your urls.py file:

from rest_framework.routers import DefaultRouter
from django_rest_passwordreset.urls import add_reset_password_urls_to_router

router = DefaultRouter()
add_reset_password_urls_to_router(router, base_path='api/auth/passwordreset')

Alternatively you can import the ViewSets manually and customize the routes for your setup:

from rest_framework.routers import DefaultRouter
from django_rest_passwordreset.views import ResetPasswordValidateTokenViewSet, ResetPasswordConfirmViewSet, \
    ResetPasswordRequestTokenViewSet

router = DefaultRouter()
router.register(
    r'api/auth/passwordreset/validate_token',
    ResetPasswordValidateTokenViewSet,
    basename='reset-password-validate'
)
router.register(
    r'api/auth/passwordreset/confirm',
    ResetPasswordConfirmViewSet,
    basename='reset-password-confirm'
)
router.register(
    r'api/auth/passwordreset/',
    ResetPasswordRequestTokenViewSet,
    basename='reset-password-request'
)

drf_browsable_email_validation

drf_browsable_password_validation

coreapi_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). We therefore had to rewrite the migration 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

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_passwordreset.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

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

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:

 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/. Basically, all endpoints are covered with multiple unit tests.

Follow below instructions to run the tests. You may exchange the installed Django and DRF versions according to your requirements. ⚠️ Depending on your local environment settings you might need to explicitly call python3 instead of python.

# install dependencies
python -m pip install --upgrade pip
pip install -r tests/requirements.txt

# setup environment
pip install -e .

# run tests
cd tests && python manage.py test

Release on PyPi

To release this package on pypi, the following steps are used:

rm -rf dist/ build/
python setup.py sdist
twine upload dist/*

More Repositories

1

winshock-test

Bash script that tests if a system is Winshock (MS14-066) vulnerable
Shell
96
star
2

fsquota

Native Go library and CLI for managing filesystem quotas
Go
25
star
3

django-request-cache

A Django app that provides a new cache on every request object. The cache is only kept within the request/response cycle.
Python
21
star
4

consulacl

Go library for working with consul ACLs
Go
18
star
5

django-rest-multitokenauth

An extension to Django-Rest-Frameworks Token Authentication, enabling a user to have multiple authorization tokens
Python
12
star
6

django-cleanhtmlfield

A Django package that cleans HTML Fields
Python
7
star
7

terraform-provider-anxcloud

Terraform Provider for Anexia Engine
Go
7
star
8

drowncheck

DROWN SSL attack checker
Shell
6
star
9

geofeed-validator

Validation library for self-published geofeeds
Python
6
star
10

go-human

Go human-readable encoding
Go
5
star
11

go-structmapper

Go library providing functionality that allows mapping structs to maps and vice versa
Go
5
star
12

talk-needctx

Code examples of need context golang talk
Go
5
star
13

go-quickstart

WeAreDevelopers 2018 - Quickstart Into Go workshop code
Go
4
star
14

go-anxcloud

Go client for Anexia Engine API
Go
3
star
15

anexia-monitoring-typo3

A TYPO3 extension used to monitor updates for TYPO3 and all installed extensions. It can be also used to check if the website is alive and working correctly.
PHP
3
star
16

anexia-laravel-encryption

PHP
2
star
17

go-cloudlogzap

CloudLog integration for go.uber.org/zap
Go
2
star
18

go-e5e

Support library to help Go developers develop Anexia e5e functions
Go
2
star
19

debian-packages

Customized Debian packages built for our infrastructure and their corresponding source packages
2
star
20

geodbtools

GeoIP database swiss army knife
Go
2
star
21

anexia-monitoring-django

A Django app used to monitor updates for Django and all installed python packages in the running environment. It can be also used to check if the website is alive and working correctly.
Python
2
star
22

go-cloudlog

CloudLog go client library
Go
2
star
23

wad2018-quotactl

WeAreDevelopers 2018 source code related to "Filesystem quota management in Go" article
Go
2
star
24

anexia-monitoring-laravel

A Laravel package used to monitor updates for core and composer packages. It can be also used to check if the website is alive and working correctly.
PHP
2
star
25

gpg2ssh

Utility for converting OpenPGP (public) keys to SSH public keys in authorized_keys format
Go
2
star
26

anexia-composer-tools

A composer package used to fetch version numbers from all installed composer packages (included in composer.json) and their latest version number registered on packagist.
PHP
1
star
27

k8s-anexia-ccm

Go
1
star
28

anexia-monitoring-wordpress

A WordPress plugin used to monitor updates for core, plugins and themes. It can be also used to check if the website is alive and working correctly.
PHP
1
star
29

ngx-ui-router-url-type-factory

A factory used to build ui-router URL types that fetch data from RESTful APIs.
TypeScript
1
star
30

hr-puzzle

hr-puzzle
1
star
31

php-cloudlog

CloudLog PHP client library
PHP
1
star
32

vue-ui-components

A Vue component library containing several components, plugins and icons that match the Anexia Corporate Design guidelines
Vue
1
star
33

anexia-laravel-basemodel

PHP
1
star