From d6a66944c52939bbe705f51c5860ead21b89d17b Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Sat, 1 Jun 2019 08:24:05 +0200 Subject: [PATCH 01/23] Metadata for release 4.1.0 --- CHANGELOG.md | 8 ++++++++ setup.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df87a4e0..91e14522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 4.1.0 + +- Expiry format now defaults to whatever is used Django REST framework +- The behavior can be overriden via EXPIRY_DATETIME_FORMAT setting +- Fully customizable expiry format via format_expiry_datetime +- Fully customizable response payload via get_post_response_data + + ## 4.0.1 - Fix for tox config to build Django 2.2 on python 3.6 diff --git a/setup.py b/setup.py index cf039c03..5e6ae73a 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='4.0.1', + version='4.1.0', description='Authentication for django rest framework', long_description=long_description, long_description_content_type='text/markdown', From 60615e2aabba1a0468b3a3f30dadddd5d6396a48 Mon Sep 17 00:00:00 2001 From: Christopher Dignam Date: Fri, 6 Sep 2019 12:37:31 -0400 Subject: [PATCH 02/23] encrypted != hashed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cbc29c98..4fef8b08 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ default implementation: an attacker unrestricted access to an account with a token if the database were compromised. - Knox tokens are only stored in an encrypted form. Even if the + Knox tokens are only stored in a secure hash form (like a password). Even if the database were somehow stolen, an attacker would not be able to log in with the stolen credentials. From 51a5204ba24494e5779467efa7172a942afcb0d1 Mon Sep 17 00:00:00 2001 From: Kawamura Shintaro Date: Mon, 16 Sep 2019 21:29:59 +0900 Subject: [PATCH 03/23] Remove salt --- docs/settings.md | 6 ------ knox/auth.py | 4 ++-- knox/crypto.py | 16 +++++----------- knox/migrations/0008_remove_authtoken_salt.py | 17 +++++++++++++++++ knox/models.py | 7 ++----- knox/settings.py | 1 - 6 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 knox/migrations/0008_remove_authtoken_salt.py diff --git a/docs/settings.md b/docs/settings.md index 4fb1b364..4964ec62 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -93,13 +93,7 @@ be raised if there is an attempt to change them. from knox.settings import CONSTANTS print(CONSTANTS.DIGEST_LENGTH) #=> 128 -print(CONSTANTS.SALT_LENGTH) #=> 16 ``` ## DIGEST_LENGTH This is the length of the digest that will be stored in the database for each token. - -## SALT_LENGTH -This is the length of the [salt][salt] that will be stored in the database for each token. - -[salt]: https://en.wikipedia.org/wiki/Salt_(cryptography) diff --git a/knox/auth.py b/knox/auth.py index ffb5a287..364c94c8 100644 --- a/knox/auth.py +++ b/knox/auth.py @@ -55,7 +55,7 @@ def authenticate(self, request): def authenticate_credentials(self, token): ''' - Due to the random nature of hashing a salted value, this must inspect + Due to the random nature of hashing a value, this must inspect each auth_token individually to find the correct one. Tokens that have expired will be deleted and skipped @@ -68,7 +68,7 @@ def authenticate_credentials(self, token): continue try: - digest = hash_token(token, auth_token.salt) + digest = hash_token(token) except (TypeError, binascii.Error): raise exceptions.AuthenticationFailed(msg) if compare_digest(digest, auth_token.digest): diff --git a/knox/crypto.py b/knox/crypto.py index 438c6b61..dba4f754 100644 --- a/knox/crypto.py +++ b/knox/crypto.py @@ -4,7 +4,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes -from knox.settings import CONSTANTS, knox_settings +from knox.settings import knox_settings sha = knox_settings.SECURE_HASH_ALGORITHM @@ -15,20 +15,14 @@ def create_token_string(): ).decode() -def create_salt_string(): - return binascii.hexlify( - generate_bytes(int(CONSTANTS.SALT_LENGTH / 2))).decode() - - -def hash_token(token, salt): +def hash_token(token): ''' - Calculates the hash of a token and salt. + Calculates the hash of a token. input is unhexlified - token and salt must contain an even number of hex digits or - a binascii.Error exception will be raised + token must contain an even number of hex digits or a binascii.Error + exception will be raised ''' digest = hashes.Hash(sha(), backend=default_backend()) digest.update(binascii.unhexlify(token)) - digest.update(binascii.unhexlify(salt)) return binascii.hexlify(digest.finalize()).decode() diff --git a/knox/migrations/0008_remove_authtoken_salt.py b/knox/migrations/0008_remove_authtoken_salt.py new file mode 100644 index 00000000..ba97bc96 --- /dev/null +++ b/knox/migrations/0008_remove_authtoken_salt.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.5 on 2019-09-16 12:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('knox', '0007_auto_20190111_0542'), + ] + + operations = [ + migrations.RemoveField( + model_name='authtoken', + name='salt', + ), + ] diff --git a/knox/models.py b/knox/models.py index 39910ec4..4dbee1ae 100644 --- a/knox/models.py +++ b/knox/models.py @@ -11,15 +11,14 @@ class AuthTokenManager(models.Manager): def create(self, user, expiry=knox_settings.TOKEN_TTL): token = crypto.create_token_string() - salt = crypto.create_salt_string() - digest = crypto.hash_token(token, salt) + digest = crypto.hash_token(token) if expiry is not None: expiry = timezone.now() + expiry instance = super(AuthTokenManager, self).create( token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH], digest=digest, - salt=salt, user=user, expiry=expiry) + user=user, expiry=expiry) return instance, token @@ -31,8 +30,6 @@ class AuthToken(models.Model): max_length=CONSTANTS.DIGEST_LENGTH, primary_key=True) token_key = models.CharField( max_length=CONSTANTS.TOKEN_KEY_LENGTH, db_index=True) - salt = models.CharField( - max_length=CONSTANTS.SALT_LENGTH, unique=True) user = models.ForeignKey(User, null=False, blank=False, related_name='auth_token_set', on_delete=models.CASCADE) created = models.DateTimeField(auto_now_add=True) diff --git a/knox/settings.py b/knox/settings.py index 179522d7..5bb14483 100644 --- a/knox/settings.py +++ b/knox/settings.py @@ -42,7 +42,6 @@ class CONSTANTS: ''' TOKEN_KEY_LENGTH = 8 DIGEST_LENGTH = 128 - SALT_LENGTH = 16 def __setattr__(self, *args, **kwargs): raise Exception(''' From 6d8daa440c218c83740181039a02a720c441904b Mon Sep 17 00:00:00 2001 From: GTpyro Date: Mon, 30 Dec 2019 11:19:06 -0600 Subject: [PATCH 04/23] Update settings.md fixed typo in api_settings.DATETIME_FORMAT --- docs/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.md b/docs/settings.md index 4fb1b364..a863a9ef 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -17,7 +17,7 @@ REST_KNOX = { 'USER_SERIALIZER': 'knox.serializers.UserSerializer', 'TOKEN_LIMIT_PER_USER': None, 'AUTO_REFRESH': False, - 'EXPIRY_DATETIME_FORMAT': api_settings.DATETME_FORMAT, + 'EXPIRY_DATETIME_FORMAT': api_settings.DATETIME_FORMAT, } #...snip... ``` From d5db277ce716cd8ea3c4762bb56d1e4688de7e7d Mon Sep 17 00:00:00 2001 From: Kees Hink Date: Sat, 8 Feb 2020 10:29:52 +0100 Subject: [PATCH 05/23] Stop supporting Python 3.4 --- .travis.yml | 2 -- setup.py | 1 - tox.ini | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 47771ed4..35c937fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,6 @@ matrix: include: - python: "2.7" env: TOX_ENVS=py27-django111 - - python: "3.4" - env: TOX_ENVS=py34-django111,py34-django20 - python: "3.5" env: TOX_ENVS=py35-django111,py35-django20,py35-django20,py35-django21 - python: "3.6" diff --git a/setup.py b/setup.py index 5e6ae73a..1ab81ef4 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,6 @@ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', diff --git a/tox.ini b/tox.ini index f80ab08d..a5ff8ae6 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ envlist = isort flake8, - py{27,34,35,36}-django111, - py{34,35,36}-django20, + py{27,35,36}-django111, + py{35,36}-django20, py{35,36,37}-django21, py{35,36,37}-django22, From 56de8df5f5afd45e320c01f654f429b8b75796bd Mon Sep 17 00:00:00 2001 From: Kees Hink Date: Sat, 8 Feb 2020 10:22:02 +0100 Subject: [PATCH 06/23] Remove unused User definition --- knox/auth.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/knox/auth.py b/knox/auth.py index ffb5a287..cc0dd733 100644 --- a/knox/auth.py +++ b/knox/auth.py @@ -6,7 +6,6 @@ def compare_digest(a, b): import binascii -from django.contrib.auth import get_user_model from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from rest_framework import exceptions @@ -19,8 +18,6 @@ def compare_digest(a, b): from knox.settings import CONSTANTS, knox_settings from knox.signals import token_expired -User = get_user_model() - class TokenAuthentication(BaseAuthentication): ''' From e1ac84012822d5f191106b097f123be7d8422b74 Mon Sep 17 00:00:00 2001 From: Kees Hink Date: Sat, 8 Feb 2020 10:11:45 +0100 Subject: [PATCH 07/23] Explain why we return None if prefix doesn't match Also, test all code paths in TokenAuthentication.authenticate() --- knox/auth.py | 5 ++++- tests/tests.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/knox/auth.py b/knox/auth.py index cc0dd733..1a2b37c4 100644 --- a/knox/auth.py +++ b/knox/auth.py @@ -37,7 +37,10 @@ def authenticate(self, request): auth = get_authorization_header(request).split() prefix = knox_settings.AUTH_HEADER_PREFIX.encode() - if not auth or auth[0].lower() != prefix.lower(): + if not auth: + return None + if auth[0].lower() != prefix.lower(): + # Authorization header is possibly for another backend return None if len(auth) == 1: msg = _('Invalid token header. No credentials provided.') diff --git a/tests/tests.py b/tests/tests.py index ffe6f2d8..efd05f9c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -5,6 +5,7 @@ from django.test import override_settings from django.utils.six.moves import reload_module from freezegun import freeze_time +from rest_framework.exceptions import AuthenticationFailed from rest_framework.serializers import DateTimeField from rest_framework.test import APIRequestFactory, APITestCase as TestCase @@ -202,6 +203,34 @@ def test_update_token_key(self): auth_token.token_key, ) + def test_authorization_header_empty(self): + rf = APIRequestFactory() + request = rf.get('/') + request.META = {'HTTP_AUTHORIZATION': ''} + self.assertEqual(TokenAuthentication().authenticate(request), None) + + def test_authorization_header_prefix_only(self): + rf = APIRequestFactory() + request = rf.get('/') + request.META = {'HTTP_AUTHORIZATION': 'Token'} + with self.assertRaises(AuthenticationFailed) as err: + (self.user, auth_token) = TokenAuthentication().authenticate(request) + self.assertIn( + 'Invalid token header. No credentials provided.', + str(err.exception), + ) + + def test_authorization_header_spaces_in_token_string(self): + rf = APIRequestFactory() + request = rf.get('/') + request.META = {'HTTP_AUTHORIZATION': 'Token wordone wordtwo'} + with self.assertRaises(AuthenticationFailed) as err: + (self.user, auth_token) = TokenAuthentication().authenticate(request) + self.assertIn( + 'Invalid token header. Token string should not contain spaces.', + str(err.exception), + ) + def test_invalid_token_length_returns_401_code(self): invalid_token = "1" * (CONSTANTS.TOKEN_KEY_LENGTH - 1) self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % invalid_token)) From 77f3374044fd94f37c75ac3f044849f687bc4427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Tue, 28 Apr 2020 11:45:07 +0200 Subject: [PATCH 08/23] Use six directly in order to work with django 3.x --- tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index efd05f9c..c50b5df1 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -3,11 +3,11 @@ from django.contrib.auth import get_user_model from django.test import override_settings -from django.utils.six.moves import reload_module from freezegun import freeze_time from rest_framework.exceptions import AuthenticationFailed from rest_framework.serializers import DateTimeField from rest_framework.test import APIRequestFactory, APITestCase as TestCase +from six.moves import reload_module from knox import auth, views from knox.auth import TokenAuthentication From 7f2e838a6fee89762fdc55bae3764a4abb13c042 Mon Sep 17 00:00:00 2001 From: Foluso Ogunlana Date: Tue, 9 Jun 2020 06:29:52 +0100 Subject: [PATCH 09/23] fix django warnings - ugettext_lazy is deprecated --- README.md | 2 +- knox/auth.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cbc29c98..71fa473e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ More information can be found in the If you need to debug a test locally and if you have [docker](https://www.docker.com/) installed: -simply run the ``./docker-run-test.sh`` script and it will run the test suite in every Python / +simply run the ``./docker-run-tests.sh`` script and it will run the test suite in every Python / Django versions. You could also simply run regular ``tox`` in the root folder as well, but that would make testing the matrix of diff --git a/knox/auth.py b/knox/auth.py index 1a2b37c4..318cc281 100644 --- a/knox/auth.py +++ b/knox/auth.py @@ -7,7 +7,7 @@ def compare_digest(a, b): import binascii from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions from rest_framework.authentication import ( BaseAuthentication, get_authorization_header, From 8989fec8c06204858fe8a475ad52acb7998a3f84 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 26 Sep 2020 01:59:44 +0200 Subject: [PATCH 10/23] add expiry column to admin view --- knox/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knox/admin.py b/knox/admin.py index e65d7059..a045f7a7 100644 --- a/knox/admin.py +++ b/knox/admin.py @@ -5,6 +5,6 @@ @admin.register(models.AuthToken) class AuthTokenAdmin(admin.ModelAdmin): - list_display = ('digest', 'user', 'created',) + list_display = ('digest', 'user', 'created', 'expiry',) fields = () raw_id_fields = ('user',) From 58418eec0154b6ca9d3a6d595e24ba9cbcbd1e4e Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 26 Sep 2020 02:40:29 +0200 Subject: [PATCH 11/23] add new python and django to test add django 3.0 3.1 add python 3.8 --- .travis.yml | 4 ++++ setup.py | 1 + tox.ini | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 35c937fe..29f36eb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,10 @@ matrix: env: TOX_ENVS=py37-django20,py37-django21,py37-django22 dist: xenial sudo: true + - python: "3.8" + env: TOX_ENVS=py38-django30,py38-django31 + dist: xenial + sudo: true before_script: - flake8 . script: diff --git a/setup.py b/setup.py index 1ab81ef4..95c241ee 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,7 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], # What does your project relate to? diff --git a/tox.ini b/tox.ini index a5ff8ae6..5db0289b 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,8 @@ envlist = py{35,36}-django20, py{35,36,37}-django21, py{35,36,37}-django22, + py{36,37,38}-django30, + py{36,37,38}-django31, [testenv:flake8] deps = flake8 @@ -33,6 +35,8 @@ deps = django20: Django>=2.0,<2.1 django21: Django>=2.1,<2.2 django22: Django>=2.2,<2.3 + django30: Django>=3.0,<3.1 + django31: Django>=3.1,<3.2 django-nose markdown<3.0 isort From 7fde60973899d12f94984b693dacae30c50dc0e5 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 26 Sep 2020 21:37:28 +0200 Subject: [PATCH 12/23] change links from http to https --- README.md | 4 ++-- docs/auth.md | 2 +- docs/index.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe41a5cf..c9f394f6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ django-rest-knox Authentication Module for django rest auth Knox provides easy to use authentication for [Django REST -Framework](http://www.django-rest-framework.org/) The aim is to allow +Framework](https://www.django-rest-framework.org/) The aim is to allow for common patterns in applications that are REST based, with little extra effort; and to ensure that connections remain secure. @@ -39,7 +39,7 @@ default implementation: the app settings (default is 10 hours.) More information can be found in the -[Documentation](http://james1345.github.io/django-rest-knox/) +[Documentation](https://james1345.github.io/django-rest-knox/) # Run the tests locally diff --git a/docs/auth.md b/docs/auth.md index 5872373a..d3e574b7 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -4,7 +4,7 @@ Knox provides one class to handle authentication. ## TokenAuthentication -This works using [DRF's authentication system](http://www.django-rest-framework.org/api-guide/authentication/). +This works using [DRF's authentication system](https://www.django-rest-framework.org/api-guide/authentication/). Knox tokens should be generated using the provided views. Any `APIView` or `ViewSet` can be accessed using these tokens by adding `TokenAuthentication` diff --git a/docs/index.md b/docs/index.md index 8a498e9f..1619797b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,5 @@ # Django-Rest-Knox -Knox provides easy to use authentication for [Django REST Framework](http://www.django-rest-framework.org/) +Knox provides easy to use authentication for [Django REST Framework](https://www.django-rest-framework.org/) The aim is to allow for common patterns in applications that are REST based, with little extra effort; and to ensure that connections remain secure. From 37474c21a0df5c16db680647e1693ff124a27abb Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 26 Sep 2020 21:48:21 +0200 Subject: [PATCH 13/23] fix typos --- CHANGELOG.md | 6 +++--- docs/installation.md | 4 ++-- docs/settings.md | 4 ++-- docs/urls.md | 2 +- docs/views.md | 2 +- tests/tests.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91e14522..fb56f9e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## 4.1.0 - Expiry format now defaults to whatever is used Django REST framework -- The behavior can be overriden via EXPIRY_DATETIME_FORMAT setting +- The behavior can be overridden via EXPIRY_DATETIME_FORMAT setting - Fully customizable expiry format via format_expiry_datetime - Fully customizable response payload via get_post_response_data @@ -71,13 +71,13 @@ Our release cycle was broken since 3.1.5, hence you can not find the previous re - Extend docs regarding usage of Token Authentication as single authentication method. ## 3.1.4 -- Fix compability with django-rest-swagger (bad inheritance) +- Fix compatibility with django-rest-swagger (bad inheritance) ## 3.1.3 - Avoid 500 error response for invalid-length token requests ## 3.1.2 -- restore compability with Python <2.7.7 +- restore compatibility with Python <2.7.7 ## 3.1.1 - use hmac.compare_digest instead of == for comparing hashes for more security diff --git a/docs/installation.md b/docs/installation.md index ad7da044..43945b44 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,7 +6,7 @@ Knox depends on `cryptography` to provide bindings to `OpenSSL` for token genera This requires the OpenSSL build libraries to be available. ### Windows -Cryptography is a statically linked build, no extra steps are needed +Cryptography is a statically linked build, no extra steps are needed. ### Linux `cryptography` should build very easily on Linux provided you have a C compiler, @@ -45,7 +45,7 @@ INSTALLED_APPS = ( ) ``` -- Make knox's TokenAuthentication your default authentification class +- Make knox's TokenAuthentication your default authentication class for django-rest-framework: ```python diff --git a/docs/settings.md b/docs/settings.md index ea613095..e44e322b 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -35,7 +35,7 @@ for production use. ### Tests SHA-512 and Whirlpool are secure, however, they are slow. This should not be a -problem for your users, but when testing it may be noticable (as test cases tend +problem for your users, but when testing it may be noticeable (as test cases tend to use many more requests much more quickly than real users). In testing scenarios it is acceptable to use `MD5` hashing.(`cryptography.hazmat.primitives.hashes.MD5`) @@ -63,7 +63,7 @@ By default this option is disabled and set to `None` -- thus no limit. ## USER_SERIALIZER This is the reference to the class used to serialize the `User` objects when -succesfully returning from `LoginView`. The default is `knox.serializers.UserSerializer` +successfully returning from `LoginView`. The default is `knox.serializers.UserSerializer` ## AUTO_REFRESH This defines if the token expiry time is extended by TOKEN_TTL each time the token diff --git a/docs/urls.md b/docs/urls.md index 95177124..6a2751cd 100644 --- a/docs/urls.md +++ b/docs/urls.md @@ -13,7 +13,7 @@ urlpatterns = [ **Note** It is important to use the string syntax and not try to import `knox.urls`, as the reference to the `User` model will cause the app to fail at import time. -The views would then acessible as: +The views would then accessible as: - `/api/auth/login` -> `LoginView` - `/api/auth/logout` -> `LogoutView` diff --git a/docs/views.md b/docs/views.md index 509c58df..c8ed762b 100644 --- a/docs/views.md +++ b/docs/views.md @@ -20,7 +20,7 @@ helper methods: - `get_token_limit_per_user(self)`, to change the number of tokens available for a user - `get_user_serializer_class(self)`, to change the class used for serializing the user - `get_expiry_datetime_format(self)`, to change the datetime format used for expiry -- `format_expiry_datetime(self, expiry)`, to format the expiry `datetime` object at your convinience +- `format_expiry_datetime(self, expiry)`, to format the expiry `datetime` object at your convenience Finally, if none of these helper methods are sufficient, you can also override `get_post_response_data` to return a fully customized payload. diff --git a/tests/tests.py b/tests/tests.py index c50b5df1..079cfcde 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -281,7 +281,7 @@ def test_token_expiry_is_extended_with_auto_refresh_activated(self): response = self.client.get(root_url, {}, format='json') self.assertEqual(response.status_code, 401) - def test_token_expiry_is_not_extended_with_auto_refresh_deativated(self): + def test_token_expiry_is_not_extended_with_auto_refresh_deactivated(self): self.assertEqual(knox_settings.AUTO_REFRESH, False) self.assertEqual(knox_settings.TOKEN_TTL, timedelta(hours=10)) From cf2f8354ed66c5ed3276733db96ea0e2ccb7f767 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Thu, 8 Oct 2020 16:30:10 +0200 Subject: [PATCH 14/23] remove deprecated key `sudo` from travis https://docs.travis-ci.com/user/build-config-validation/#deprecated_key --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29f36eb3..bdafdc01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,16 +12,13 @@ matrix: env: TOX_ENVS=py36-django111,py36-django20,py36-django21 - python: "3.6" dist: xenial - sudo: true env: TOX_ENVS=py36-django22 - python: "3.7" env: TOX_ENVS=py37-django20,py37-django21,py37-django22 dist: xenial - sudo: true - python: "3.8" env: TOX_ENVS=py38-django30,py38-django31 dist: xenial - sudo: true before_script: - flake8 . script: From c04af85ccbfa63339b4987a3f794987deda6ba77 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Mon, 12 Oct 2020 15:57:15 +0200 Subject: [PATCH 15/23] remove outdated key value dist: xenial --- .travis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index bdafdc01..493cd617 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,16 +9,11 @@ matrix: - python: "3.5" env: TOX_ENVS=py35-django111,py35-django20,py35-django20,py35-django21 - python: "3.6" - env: TOX_ENVS=py36-django111,py36-django20,py36-django21 - - python: "3.6" - dist: xenial - env: TOX_ENVS=py36-django22 + env: TOX_ENVS=py36-django111,py36-django20,py36-django21,py36-django22 - python: "3.7" env: TOX_ENVS=py37-django20,py37-django21,py37-django22 - dist: xenial - python: "3.8" env: TOX_ENVS=py38-django30,py38-django31 - dist: xenial before_script: - flake8 . script: From 0eec694365578ea82c8cca463de88abb045ea7a8 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Tue, 13 Oct 2020 14:01:52 +0200 Subject: [PATCH 16/23] drop support of eol django and python versions --- .travis.yml | 8 +++----- setup.py | 8 +++++--- tox.ini | 6 ------ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 493cd617..0307d153 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,12 @@ install: - pip install flake8 matrix: include: - - python: "2.7" - env: TOX_ENVS=py27-django111 - python: "3.5" - env: TOX_ENVS=py35-django111,py35-django20,py35-django20,py35-django21 + env: TOX_ENVS=py35-django22 - python: "3.6" - env: TOX_ENVS=py36-django111,py36-django20,py36-django21,py36-django22 + env: TOX_ENVS=py36-django22,py36-django30,py36-django31 - python: "3.7" - env: TOX_ENVS=py37-django20,py37-django21,py37-django22 + env: TOX_ENVS=py37-django22,py37-django30,py37-django31 - python: "3.8" env: TOX_ENVS=py38-django30,py38-django31 before_script: diff --git a/setup.py b/setup.py index 95c241ee..c180ae54 100644 --- a/setup.py +++ b/setup.py @@ -46,8 +46,6 @@ # Pick your license as you wish (should match "license" above) 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -67,7 +65,11 @@ # your project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/requirements.html - install_requires=['django', 'djangorestframework', 'cryptography'], + install_requires=[ + 'django>=2.2', + 'djangorestframework', + 'cryptography', + ], # List additional groups of dependencies here (e.g. development # dependencies). You can install these using the following syntax, diff --git a/tox.ini b/tox.ini index 5db0289b..cbcc7a95 100644 --- a/tox.ini +++ b/tox.ini @@ -2,9 +2,6 @@ envlist = isort flake8, - py{27,35,36}-django111, - py{35,36}-django20, - py{35,36,37}-django21, py{35,36,37}-django22, py{36,37,38}-django30, py{36,37,38}-django31, @@ -31,9 +28,6 @@ setenv = DJANGO_SETTINGS_MODULE = knox_project.settings PIP_INDEX_URL = https://pypi.python.org/simple/ deps = - django111: Django>=1.11,<2.0 - django20: Django>=2.0,<2.1 - django21: Django>=2.1,<2.2 django22: Django>=2.2,<2.3 django30: Django>=3.0,<3.1 django31: Django>=3.1,<3.2 From 66e8821baa92510657f9252430af0a4ce136ce91 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 31 Oct 2020 15:41:42 +0100 Subject: [PATCH 17/23] add python 3.9, remove eol 3.5 --- .travis.yml | 6 +++--- setup.py | 2 +- tox.ini | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0307d153..5e0827bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,14 +4,14 @@ install: - pip install flake8 matrix: include: - - python: "3.5" - env: TOX_ENVS=py35-django22 - python: "3.6" env: TOX_ENVS=py36-django22,py36-django30,py36-django31 - python: "3.7" env: TOX_ENVS=py37-django22,py37-django30,py37-django31 - python: "3.8" - env: TOX_ENVS=py38-django30,py38-django31 + env: TOX_ENVS=py38-django22,py38-django30,py38-django31 + - python: "3.9" + env: TOX_ENVS=py39-django22,py39-django30,py39-django31 before_script: - flake8 . script: diff --git a/setup.py b/setup.py index c180ae54..52f5f2c9 100644 --- a/setup.py +++ b/setup.py @@ -47,10 +47,10 @@ # Pick your license as you wish (should match "license" above) 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], # What does your project relate to? diff --git a/tox.ini b/tox.ini index cbcc7a95..43901d95 100644 --- a/tox.ini +++ b/tox.ini @@ -2,9 +2,9 @@ envlist = isort flake8, - py{35,36,37}-django22, - py{36,37,38}-django30, - py{36,37,38}-django31, + py{36,37,38,39}-django22, + py{36,37,38,39}-django30, + py{36,37,38,39}-django31, [testenv:flake8] deps = flake8 From 2e6fe6b524b9173290a4aa6fd76b835473c4d913 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 31 Oct 2020 18:56:33 +0100 Subject: [PATCH 18/23] add python requirement --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 52f5f2c9..501449fd 100644 --- a/setup.py +++ b/setup.py @@ -65,6 +65,7 @@ # your project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/requirements.html + python_requires='>=3.6', install_requires=[ 'django>=2.2', 'djangorestframework', From 17e4a5cc1d74113b77b3b2a295afa50b320d65aa Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Tue, 3 Nov 2020 02:44:16 +0100 Subject: [PATCH 19/23] remove switch for django 1.x --- knox_project/settings.py | 1 - knox_project/urls.py | 8 +------- tests/tests.py | 8 +------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/knox_project/settings.py b/knox_project/settings.py index f0378fcd..62725fa3 100644 --- a/knox_project/settings.py +++ b/knox_project/settings.py @@ -18,7 +18,6 @@ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.middleware.security.SecurityMiddleware', ) diff --git a/knox_project/urls.py b/knox_project/urls.py index 3fb1a8ed..fa9ccfc8 100644 --- a/knox_project/urls.py +++ b/knox_project/urls.py @@ -1,10 +1,4 @@ -try: - # For django >= 2.0 - from django.urls import include, re_path -except ImportError: - # For django < 2.0 - from django.conf.urls import include, url - re_path = url +from django.urls import include, re_path from .views import RootView diff --git a/tests/tests.py b/tests/tests.py index c50b5df1..515963b7 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -3,6 +3,7 @@ from django.contrib.auth import get_user_model from django.test import override_settings +from django.urls import reverse from freezegun import freeze_time from rest_framework.exceptions import AuthenticationFailed from rest_framework.serializers import DateTimeField @@ -16,13 +17,6 @@ from knox.settings import CONSTANTS, knox_settings from knox.signals import token_expired -try: - # For django >= 2.0 - from django.urls import reverse -except ImportError: - # For django < 2.0 - from django.conf.urls import reverse - User = get_user_model() root_url = reverse('api-root') From c14bab689cdab8a2c4b357ec0fda7d38888185d8 Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sun, 15 Nov 2020 20:43:43 +0100 Subject: [PATCH 20/23] fix deprecation of djangos url() #237 RemovedInDjango40Warning: django.conf.urls.url() is deprecated in favor of django.urls.re_path(). --- docs/auth.md | 12 ++++++------ docs/urls.md | 2 +- knox/urls.py | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/auth.md b/docs/auth.md index d3e574b7..63828ed9 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -65,9 +65,9 @@ from knox import views as knox_views from yourapp.api.views import LoginView urlpatterns = [ - url(r'login/', LoginView.as_view(), name='knox_login'), - url(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'), - url(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'), + path(r'login/', LoginView.as_view(), name='knox_login'), + path(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'), + path(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'), ] ``` @@ -101,8 +101,8 @@ from knox import views as knox_views from yourapp.api.views import LoginView urlpatterns = [ - url(r'login/', LoginView.as_view(), name='knox_login'), - url(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'), - url(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'), + path(r'login/', LoginView.as_view(), name='knox_login'), + path(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'), + path(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'), ] ``` \ No newline at end of file diff --git a/docs/urls.md b/docs/urls.md index 6a2751cd..628f8747 100644 --- a/docs/urls.md +++ b/docs/urls.md @@ -6,7 +6,7 @@ This can easily be included in your url config: ```python urlpatterns = [ #...snip... - url(r'api/auth/', include('knox.urls')) + path(r'api/auth/', include('knox.urls')) #...snip... ] ``` diff --git a/knox/urls.py b/knox/urls.py index d3506c3f..e3ac58c8 100644 --- a/knox/urls.py +++ b/knox/urls.py @@ -1,9 +1,9 @@ -from django.conf.urls import url +from django.urls import path from knox import views urlpatterns = [ - url(r'login/', views.LoginView.as_view(), name='knox_login'), - url(r'logout/', views.LogoutView.as_view(), name='knox_logout'), - url(r'logoutall/', views.LogoutAllView.as_view(), name='knox_logoutall'), + path(r'login/', views.LoginView.as_view(), name='knox_login'), + path(r'logout/', views.LogoutView.as_view(), name='knox_logout'), + path(r'logoutall/', views.LogoutAllView.as_view(), name='knox_logoutall'), ] From e8920d7edfecca8c4bfae245c8a7bf1984bc1ff6 Mon Sep 17 00:00:00 2001 From: Drew Budwin Date: Tue, 10 Nov 2020 15:17:40 -0500 Subject: [PATCH 21/23] Fix Django deprecation warning about Signals --- knox/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knox/signals.py b/knox/signals.py index ad8a7538..3c644fc6 100644 --- a/knox/signals.py +++ b/knox/signals.py @@ -1,3 +1,3 @@ import django.dispatch -token_expired = django.dispatch.Signal(providing_args=["username", "source"]) +token_expired = django.dispatch.Signal() From 7abaefaa762f10385bfe27a2d5ecfe92c3921ecb Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 15 Jan 2022 16:26:49 +0100 Subject: [PATCH 22/23] Update tests for current django & python versions (#261) * Change to github actions as CI * update python and django version remove and add outdated python and django versions * remove deprecated isort settings * ensure expired token for test sometimes tests failed on windows * Remove deprecated `django-nose` --- .github/workflows/test.yml | 43 ++++++++++++++++++++++++++++++++++++++ .isort.cfg | 1 - .travis.yml | 30 -------------------------- README.md | 2 +- knox_project/settings.py | 3 --- setup.py | 4 ++-- tests/tests.py | 10 ++++----- tox.ini | 25 +++++++++++++--------- 8 files changed, 66 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..379d39cc --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Tox tests + run: | + tox -v diff --git a/.isort.cfg b/.isort.cfg index 54a79a7f..310512e9 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -4,4 +4,3 @@ default_section = THIRDPARTY include_trailing_comma = true known_first_party = knox multi_line_output = 5 -not_skip = __init__.py diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5e0827bf..00000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: python -install: - - pip install tox - - pip install flake8 -matrix: - include: - - python: "3.6" - env: TOX_ENVS=py36-django22,py36-django30,py36-django31 - - python: "3.7" - env: TOX_ENVS=py37-django22,py37-django30,py37-django31 - - python: "3.8" - env: TOX_ENVS=py38-django22,py38-django30,py38-django31 - - python: "3.9" - env: TOX_ENVS=py39-django22,py39-django30,py39-django31 -before_script: - - flake8 . -script: - - tox -e $TOX_ENVS -deploy: - provider: pypi - user: james.mcmahon - password: testpasswordfortravis - on: - tags: true - repo: James1345/django-rest-knox - only: - - master - distributions: "sdist bdist_wheel" -git: - depth: false diff --git a/README.md b/README.md index c9f394f6..203db1f1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ django-rest-knox ================ -[![image](https://travis-ci.org/James1345/django-rest-knox.svg?branch=develop)](https://travis-ci.org/James1345/django-rest-knox) +[![image](https://github.com/James1345/django-rest-knox/workflows/Test/badge.svg?branch=develop)](https://github.com/James1345/django-rest-knox/actions) Authentication Module for django rest auth diff --git a/knox_project/settings.py b/knox_project/settings.py index 62725fa3..85dcc48d 100644 --- a/knox_project/settings.py +++ b/knox_project/settings.py @@ -10,7 +10,6 @@ 'django.contrib.sessions', 'rest_framework', 'knox', - 'django_nose', ) MIDDLEWARE_CLASSES = ( @@ -54,5 +53,3 @@ USE_TZ = True STATIC_URL = '/static/' - -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' diff --git a/setup.py b/setup.py index 501449fd..f2bc616c 100644 --- a/setup.py +++ b/setup.py @@ -47,10 +47,10 @@ # Pick your license as you wish (should match "license" above) 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ], # What does your project relate to? @@ -67,7 +67,7 @@ # https://packaging.python.org/en/latest/requirements.html python_requires='>=3.6', install_requires=[ - 'django>=2.2', + 'django>=3.2', 'djangorestframework', 'cryptography', ], diff --git a/tests/tests.py b/tests/tests.py index db942602..914ce204 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -165,7 +165,7 @@ def test_logout_all_deletes_only_targets_keys(self): def test_expired_tokens_login_fails(self): self.assertEqual(AuthToken.objects.count(), 0) instance, token = AuthToken.objects.create( - user=self.user, expiry=timedelta(seconds=0)) + user=self.user, expiry=timedelta(seconds=-1)) self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token)) response = self.client.post(root_url, {}, format='json') self.assertEqual(response.status_code, 401) @@ -174,9 +174,9 @@ def test_expired_tokens_login_fails(self): def test_expired_tokens_deleted(self): self.assertEqual(AuthToken.objects.count(), 0) for _ in range(10): - # 0 TTL gives an expired token + # -1 TTL gives an expired token instance, token = AuthToken.objects.create( - user=self.user, expiry=timedelta(seconds=0)) + user=self.user, expiry=timedelta(seconds=-1)) self.assertEqual(AuthToken.objects.count(), 10) # Attempting a single logout should delete all tokens @@ -318,7 +318,7 @@ def handler(sender, username, **kwargs): token_expired.connect(handler) - instance, token = AuthToken.objects.create(user=self.user, expiry=timedelta(0)) + instance, token = AuthToken.objects.create(user=self.user, expiry=timedelta(seconds=-1)) self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token)) self.client.post(root_url, {}, format='json') @@ -346,7 +346,7 @@ def test_does_not_exceed_on_expired_keys(self): reload_module(views) for _ in range(9): AuthToken.objects.create(user=self.user) - AuthToken.objects.create(user=self.user, expiry=timedelta(seconds=0)) + AuthToken.objects.create(user=self.user, expiry=timedelta(seconds=-1)) # now 10 keys, but only 9 valid so request should succeed. url = reverse('knox_login') self.client.credentials( diff --git a/tox.ini b/tox.ini index 43901d95..c6610d86 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] envlist = - isort + isort, flake8, - py{36,37,38,39}-django22, - py{36,37,38,39}-django30, - py{36,37,38,39}-django31, + py{36,37,38,39,310}-django32, + py{38,39,310}-django40, [testenv:flake8] deps = flake8 @@ -14,7 +13,7 @@ commands = flake8 knox [testenv:isort] deps = isort changedir = {toxinidir} -commands = isort --recursive --check-only --diff \ +commands = isort --check-only --diff \ knox \ knox_project/views.py \ setup.py \ @@ -28,12 +27,10 @@ setenv = DJANGO_SETTINGS_MODULE = knox_project.settings PIP_INDEX_URL = https://pypi.python.org/simple/ deps = - django22: Django>=2.2,<2.3 - django30: Django>=3.0,<3.1 - django31: Django>=3.1,<3.2 - django-nose + django32: Django>=3.2,<3.3 + django40: Django>=4.0,<4.1 markdown<3.0 - isort + isort>=5.0 djangorestframework freezegun mkdocs @@ -42,3 +39,11 @@ deps = setuptools twine wheel + +[gh-actions] +python = + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39, isort, flake8 + 3.10: py310 From 6cbf52afb32f22ececb3afe036494715ea383f21 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Mon, 31 Jan 2022 16:23:30 +0100 Subject: [PATCH 23/23] Release notes for version 4.2 --- CHANGELOG.md | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb56f9e3..96a31be7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.2.0 +- compatibility with Python up to 3.10 and Django up to 3.2 +- integration with github CI instead of travis +- Migration: "salt" field of model "AuthToken" is removed + ## 4.1.0 - Expiry format now defaults to whatever is used Django REST framework diff --git a/setup.py b/setup.py index f2bc616c..30d63bdb 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='4.1.0', + version='4.2.0', description='Authentication for django rest framework', long_description=long_description, long_description_content_type='text/markdown',