Skip to content

Commit

Permalink
Merge pull request #264 from James1345/develop
Browse files Browse the repository at this point in the history
Merge develop to master for release 4.2
  • Loading branch information
belugame authored Jan 31, 2022
2 parents 496f34d + 6cbf52a commit c48493d
Show file tree
Hide file tree
Showing 24 changed files with 171 additions and 142 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ default_section = THIRDPARTY
include_trailing_comma = true
known_first_party = knox
multi_line_output = 5
not_skip = __init__.py
38 changes: 0 additions & 38 deletions .travis.yml

This file was deleted.

11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
## 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
- 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

Expand Down Expand Up @@ -71,13 +76,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
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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

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.

Expand All @@ -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.

Expand All @@ -39,13 +39,13 @@ 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

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
Expand Down
14 changes: 7 additions & 7 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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'),
]
```

Expand Down Expand Up @@ -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'),
]
```
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
4 changes: 2 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
10 changes: 2 additions & 8 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
4 changes: 2 additions & 2 deletions docs/urls.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ 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...
]
```
**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`
Expand Down
2 changes: 1 addition & 1 deletion docs/views.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion knox/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',)
14 changes: 7 additions & 7 deletions knox/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ 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 django.utils.translation import gettext_lazy as _
from rest_framework import exceptions
from rest_framework.authentication import (
BaseAuthentication, get_authorization_header,
Expand All @@ -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):
'''
Expand All @@ -40,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.')
Expand All @@ -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
Expand All @@ -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):
Expand Down
16 changes: 5 additions & 11 deletions knox/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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()
17 changes: 17 additions & 0 deletions knox/migrations/0008_remove_authtoken_salt.py
Original file line number Diff line number Diff line change
@@ -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',
),
]
Loading

0 comments on commit c48493d

Please sign in to comment.