Skip to content

Commit 87da8d2

Browse files
authored
Merge branch 'develop' into patch-1
2 parents 04c15fd + 0940325 commit 87da8d2

25 files changed

+340
-131
lines changed

.codecov.yml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
coverage:
2+
status:
3+
project:
4+
default: false
5+
tests:
6+
paths: tests
7+
informational: true
8+
knox:
9+
paths: knox
10+
informational: true
11+
patch: off

.coveragerc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[run]
2+
branch = True
3+
source = knox
4+
omit =
5+
*/migrations/*

.github/workflows/test.yml

+12-6
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ jobs:
99
fail-fast: false
1010
max-parallel: 5
1111
matrix:
12-
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
12+
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
1313

1414
steps:
15-
- uses: actions/checkout@v2
15+
- uses: actions/checkout@v4
1616

1717
- name: Set up Python ${{ matrix.python-version }}
18-
uses: actions/setup-python@v2
18+
uses: actions/setup-python@v4
1919
with:
2020
python-version: ${{ matrix.python-version }}
2121

2222
- name: Get pip cache dir
2323
id: pip-cache
2424
run: |
25-
echo "::set-output name=dir::$(pip cache dir)"
25+
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
2626
2727
- name: Cache
28-
uses: actions/cache@v2
28+
uses: actions/cache@v3
2929
with:
3030
path: ${{ steps.pip-cache.outputs.dir }}
3131
key:
@@ -36,8 +36,14 @@ jobs:
3636
- name: Install dependencies
3737
run: |
3838
python -m pip install --upgrade pip
39-
python -m pip install --upgrade tox tox-gh-actions
39+
python -m pip install --upgrade tox tox-gh-actions coverage
4040
4141
- name: Tox tests
4242
run: |
4343
tox -v
44+
45+
- name: Generate coverage XML report
46+
run: coverage xml
47+
48+
- name: Codecov
49+
uses: codecov/codecov-action@v3

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,6 @@ docs/_build/
5757
target/
5858
db.sqlite3
5959
site/
60+
61+
# PyCharm Project
62+
.idea

.pre-commit-config.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
repos:
2+
- repo: https://github.com/PyCQA/isort
3+
rev: 5.13.2
4+
hooks:
5+
- id: isort
6+
- repo: https://github.com/PyCQA/flake8
7+
rev: 7.0.0
8+
hooks:
9+
- id: flake8

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## 4.2.0
22
- compatibility with Python up to 3.10 and Django up to 4.0
33
- integration with github CI instead of travis
4-
- Migration: "salt" field of model "AuthToken" is removed
4+
- Migration: "salt" field of model "AuthToken" is removed, WARNING: invalidates old tokens!
55

66
## 4.1.0
77

CONTRIBUTING.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/)
2+
3+
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the [guidelines](https://jazzband.co/about/guidelines).

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
django-rest-knox
22
================
33

4-
[![image](https://github.com/James1345/django-rest-knox/workflows/Test/badge.svg?branch=develop)](https://github.com/James1345/django-rest-knox/actions)
4+
[![Jazzband](https://jazzband.co/static/img/badge.svg)](https://jazzband.co/)
5+
[![image](https://github.com/jazzband/django-rest-knox/workflows/Test/badge.svg?branch=develop)](https://github.com/jazzband/django-rest-knox/actions)
56

6-
Authentication Module for django rest auth
7+
Authentication module for Django rest auth.
78

89
Knox provides easy-to-use authentication for [Django REST
910
Framework](https://www.django-rest-framework.org/) The aim is to allow
@@ -40,12 +41,11 @@ default implementation:
4041
the app settings (default is 10 hours.)
4142

4243
More information can be found in the
43-
[Documentation](https://james1345.github.io/django-rest-knox/)
44+
[Documentation](https://jazzband.github.io/django-rest-knox/)
4445

4546
# Run the tests locally
4647

47-
If you need to debug a test locally and if you have [docker](https://www.docker.com/) installed:
48-
48+
If you need to debug a test locally and if you have [docker](https://www.docker.com/) installed,
4949
simply run the ``./docker-run-tests.sh`` script and it will run the test suite in every Python /
5050
Django versions.
5151

docs/auth.md

+8-10
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,17 @@ If it is your only default authentication class, remember to overwrite knox's Lo
4949

5050
For instance, you can authenticate users using Basic Authentication by simply overwriting knox's LoginView and setting BasicAuthentication as one of the acceptable authentication classes, as follows:
5151

52+
**views.py:**
5253
```python
53-
54-
views.py:
55-
5654
from knox.views import LoginView as KnoxLoginView
5755
from rest_framework.authentication import BasicAuthentication
5856

5957
class LoginView(KnoxLoginView):
6058
authentication_classes = [BasicAuthentication]
59+
```
6160

62-
urls.py:
63-
61+
**urls.py:**
62+
```python
6463
from knox import views as knox_views
6564
from yourapp.api.views import LoginView
6665

@@ -75,10 +74,8 @@ You can use any number of authentication classes if you want to be able to authe
7574

7675
If you decide to use Token Authentication as your only authentication class, you can overwrite knox's login view as such:
7776

77+
**views.py:**
7878
```python
79-
80-
views.py:
81-
8279
from django.contrib.auth import login
8380

8481
from rest_framework import permissions
@@ -94,9 +91,10 @@ class LoginView(KnoxLoginView):
9491
user = serializer.validated_data['user']
9592
login(request, user)
9693
return super(LoginView, self).post(request, format=None)
94+
```
9795

98-
urls.py:
99-
96+
**urls.py:**
97+
```python
10098
from knox import views as knox_views
10199
from yourapp.api.views import LoginView
102100

docs/installation.md

+3-22
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,8 @@
22

33
## Requirements
44

5-
Knox depends on `cryptography` to provide bindings to `OpenSSL` for token generation
6-
This requires the OpenSSL build libraries to be available.
7-
8-
### Windows
9-
Cryptography is a statically linked build, no extra steps are needed.
10-
11-
### Linux
12-
`cryptography` should build very easily on Linux provided you have a C compiler,
13-
headers for Python (if you’re not using `pypy`), and headers for the OpenSSL and
14-
`libffi` libraries available on your system.
15-
16-
Debian and Ubuntu:
17-
```bash
18-
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev python-dev
19-
```
20-
21-
Fedora and RHEL-derivatives:
22-
```bash
23-
sudo yum install gcc libffi-devel python-devel openssl-devel
24-
```
25-
For other systems or problems, see the [cryptography installation docs](https://cryptography.io/en/latest/installation/)
5+
Knox depends on pythons internal library `hashlib` to provide bindings to `OpenSSL` or uses
6+
an internal implementation of hashing algorithms for token generation.
267

278
## Installing Knox
289
Knox should be installed with pip
@@ -59,7 +40,7 @@ REST_FRAMEWORK = {
5940

6041
- If you set TokenAuthentication as the only default authentication class on the second step, [override knox's LoginView](auth.md#global-usage-on-all-views) to accept another authentication method and use it instead of knox's default login view.
6142

62-
- Apply the migrations for the models
43+
- Apply the migrations for the models.
6344

6445
```bash
6546
python manage.py migrate

docs/settings.md

+27-6
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,27 @@ Example `settings.py`
1010
# These are the default values if none are set
1111
from datetime import timedelta
1212
from rest_framework.settings import api_settings
13+
14+
KNOX_TOKEN_MODEL = 'knox.AuthToken'
15+
1316
REST_KNOX = {
14-
'SECURE_HASH_ALGORITHM': 'cryptography.hazmat.primitives.hashes.SHA512',
17+
'SECURE_HASH_ALGORITHM': 'hashlib.sha512',
1518
'AUTH_TOKEN_CHARACTER_LENGTH': 64,
1619
'TOKEN_TTL': timedelta(hours=10),
1720
'USER_SERIALIZER': 'knox.serializers.UserSerializer',
1821
'TOKEN_LIMIT_PER_USER': None,
1922
'AUTO_REFRESH': False,
23+
'MIN_REFRESH_INTERVAL': 60,
24+
'AUTH_HEADER_PREFIX': 'Token',
2025
'EXPIRY_DATETIME_FORMAT': api_settings.DATETIME_FORMAT,
26+
'TOKEN_MODEL': 'knox.AuthToken',
2127
}
2228
#...snip...
2329
```
2430

31+
## KNOX_TOKEN_MODEL
32+
This is the variable used in the swappable dependency of the `AuthToken` model
33+
2534
## SECURE_HASH_ALGORITHM
2635
This is a reference to the class used to provide the hashing algorithm for
2736
token storage.
@@ -30,14 +39,13 @@ token storage.
3039

3140
By default, Knox uses SHA-512 to hash tokens in the database.
3241

33-
`cryptography.hazmat.primitives.hashes.Whirlpool` is an acceptable alternative setting
34-
for production use.
42+
`hashlib.sha3_512` is an acceptable alternative setting for production use.
3543

3644
### Tests
37-
SHA-512 and Whirlpool are secure, however, they are slow. This should not be a
45+
SHA-512 and SHA3-512 are secure, however, they are slow. This should not be a
3846
problem for your users, but when testing it may be noticeable (as test cases tend
3947
to use many more requests much more quickly than real users). In testing scenarios
40-
it is acceptable to use `MD5` hashing.(`cryptography.hazmat.primitives.hashes.MD5`)
48+
it is acceptable to use `MD5` hashing (`hashlib.md5`).
4149

4250
MD5 is **not secure** and must *never* be used in production sites.
4351

@@ -58,7 +66,8 @@ Warning: setting a 0 or negative timedelta will create tokens that instantly exp
5866
the system will not prevent you setting this.
5967

6068
## TOKEN_LIMIT_PER_USER
61-
This allows you to control how many tokens can be issued per user.
69+
This allows you to control how many valid tokens can be issued per user.
70+
If the limit for valid tokens is reached, an error is returned at login.
6271
By default this option is disabled and set to `None` -- thus no limit.
6372

6473
## USER_SERIALIZER
@@ -81,9 +90,18 @@ This is the expiry datetime format returned in the login view. The default is th
8190
[DATETIME_FORMAT][DATETIME_FORMAT] of Django REST framework. May be any of `None`, `iso-8601`
8291
or a Python [strftime format][strftime format] string.
8392

93+
## TOKEN_MODEL
94+
This is the reference to the model used as `AuthToken`. We can define a custom `AuthToken`
95+
model in our project that extends `knox.AbstractAuthToken` and add our business logic to it.
96+
The default is `knox.AuthToken`
97+
8498
[DATETIME_FORMAT]: https://www.django-rest-framework.org/api-guide/settings/#date-and-time-formatting
8599
[strftime format]: https://docs.python.org/3/library/time.html#time.strftime
86100

101+
## TOKEN_PREFIX
102+
This is the prefix for the generated token that is used in the Authorization header. The default is just an empty string.
103+
It can be up to `CONSTANTS.MAXIMUM_TOKEN_PREFIX_LENGTH` long.
104+
87105
# Constants `knox.settings`
88106
Knox also provides some constants for information. These must not be changed in
89107
external code; they are used in the model definitions in knox and an error will
@@ -97,3 +115,6 @@ print(CONSTANTS.DIGEST_LENGTH) #=> 128
97115

98116
## DIGEST_LENGTH
99117
This is the length of the digest that will be stored in the database for each token.
118+
119+
## MAXIMUM_TOKEN_PREFIX_LENGTH
120+
This is the maximum length of the token prefix.

docs/views.md

+16-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ helper methods:
2121
- `get_user_serializer_class(self)`, to change the class used for serializing the user
2222
- `get_expiry_datetime_format(self)`, to change the datetime format used for expiry
2323
- `format_expiry_datetime(self, expiry)`, to format the expiry `datetime` object at your convenience
24+
- `create_token(self)`, to create the `AuthToken` instance at your convenience
2425

2526
Finally, if none of these helper methods are sufficient, you can also override `get_post_response_data`
2627
to return a fully customized payload.
@@ -66,12 +67,24 @@ It responds to Knox Token Authentication. On a successful request,
6667
the token used to authenticate is deleted from the
6768
system and can no longer be used to authenticate.
6869

70+
By default, this endpoint returns a HTTP 204 response on a successful request. To
71+
customize this behavior, you can override the `get_post_response` method, for example
72+
to include a body in the logout response and/or to modify the status code:
73+
74+
```python
75+
...snip...
76+
def get_post_response(self, request):
77+
return Response({"bye-bye": request.user.username}, status=200)
78+
...snip...
79+
```
80+
6981
## LogoutAllView
7082
This view accepts only a post request with an empty body. It responds to Knox Token
7183
Authentication.
72-
On a successful request, the token used to authenticate, and *all other tokens*
73-
registered to the same `User` account, are deleted from the
74-
system and can no longer be used to authenticate.
84+
On a successful request, a HTTP 204 is returned and the token used to authenticate,
85+
and *all other tokens* registered to the same `User` account, are deleted from the
86+
system and can no longer be used to authenticate. The success response can be modified
87+
like the `LogoutView` by overriding the `get_post_response` method.
7588

7689
**Note** It is not recommended to alter the Logout views. They are designed
7790
specifically for token management, and to respond to Knox authentication.

knox/auth.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
try:
2-
from hmac import compare_digest
3-
except ImportError:
4-
def compare_digest(a, b):
5-
return a == b
6-
71
import binascii
2+
from hmac import compare_digest
83

94
from django.utils import timezone
105
from django.utils.translation import gettext_lazy as _
@@ -14,7 +9,7 @@ def compare_digest(a, b):
149
)
1510

1611
from knox.crypto import hash_token
17-
from knox.models import AuthToken
12+
from knox.models import get_token_model
1813
from knox.settings import CONSTANTS, knox_settings
1914
from knox.signals import token_expired
2015

@@ -31,11 +26,10 @@ class TokenAuthentication(BaseAuthentication):
3126
- `request.user` will be a django `User` instance
3227
- `request.auth` will be an `AuthToken` instance
3328
'''
34-
model = AuthToken
3529

3630
def authenticate(self, request):
3731
auth = get_authorization_header(request).split()
38-
prefix = knox_settings.AUTH_HEADER_PREFIX.encode()
32+
prefix = self.authenticate_header(request).encode()
3933

4034
if not auth:
4135
return None
@@ -62,7 +56,7 @@ def authenticate_credentials(self, token):
6256
'''
6357
msg = _('Invalid token.')
6458
token = token.decode("utf-8")
65-
for auth_token in AuthToken.objects.filter(
59+
for auth_token in get_token_model().objects.filter(
6660
token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH]):
6761
if self._cleanup_token(auth_token):
6862
continue
@@ -77,7 +71,7 @@ def authenticate_credentials(self, token):
7771
return self.validate_user(auth_token)
7872
raise exceptions.AuthenticationFailed(msg)
7973

80-
def renew_token(self, auth_token):
74+
def renew_token(self, auth_token) -> None:
8175
current_expiry = auth_token.expiry
8276
new_expiry = timezone.now() + knox_settings.TOKEN_TTL
8377
auth_token.expiry = new_expiry
@@ -95,7 +89,7 @@ def validate_user(self, auth_token):
9589
def authenticate_header(self, request):
9690
return knox_settings.AUTH_HEADER_PREFIX
9791

98-
def _cleanup_token(self, auth_token):
92+
def _cleanup_token(self, auth_token) -> bool:
9993
for other_token in auth_token.user.auth_token_set.all():
10094
if other_token.digest != auth_token.digest and other_token.expiry:
10195
if other_token.expiry < timezone.now():

0 commit comments

Comments
 (0)