Skip to content

Commit

Permalink
Merge pull request #93 from James1345/develop
Browse files Browse the repository at this point in the history
3.1.3 release
  • Loading branch information
belugame authored Feb 26, 2018
2 parents 45be9d1 + 65ebb2b commit 07d8605
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 34 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
######
3.1.3
######
- Avoid 500 error response for invalid-length token requests

######
3.1.2
######
Expand Down Expand Up @@ -65,7 +70,7 @@
######
2.2.1
######
**Please be aware: updating to his version requires applying a database migration**
**Please be aware: updating to this version requires applying a database migration**

- Introducing token_key to avoid loop over all tokens on login-requests
- Signals are sent on login/logout
Expand Down
3 changes: 3 additions & 0 deletions docs/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#Changelog

## 3.1.3
- Avoid 500 error response for invalid-length token requests

## 3.1.2
- restore compability with Python <2.7.7

Expand Down
4 changes: 3 additions & 1 deletion knox/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
def compare_digest(a, b):
return a == b

import binascii

from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
Expand Down Expand Up @@ -72,7 +74,7 @@ def authenticate_credentials(self, token):
continue
try:
digest = hash_token(token, auth_token.salt)
except TypeError:
except (TypeError, binascii.Error):
raise exceptions.AuthenticationFailed(msg)
if compare_digest(digest, auth_token.digest):
return self.validate_user(auth_token)
Expand Down
3 changes: 3 additions & 0 deletions knox/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def hash_token(token, salt):
'''
Calculates the hash of a token and salt.
input is unhexlified
token and salt 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))
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,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='3.1.2',
version='3.1.3',
description='Authentication for django rest framework',
long_description=long_description,

Expand Down
56 changes: 25 additions & 31 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ def get_basic_auth_header(username, password):

class AuthTestCase(TestCase):

def setUp(self):
self.username, self.email, self.password = 'john.doe', '[email protected]', 'hunter2'
self.user = User.objects.create_user(self.username, self.email, self.password)

self.username2, self.email2, self.password2 = 'jane.doe', '[email protected]', 'hunter2'
self.user2 = User.objects.create_user(self.username2, self.email2, self.password2)

def test_login_creates_keys(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
User.objects.create_user(username, '[email protected]', password)
url = reverse('knox_login')
self.client.credentials(
HTTP_AUTHORIZATION=get_basic_auth_header(username, password))
HTTP_AUTHORIZATION=get_basic_auth_header(self.username, self.password))

for _ in range(5):
self.client.post(url, {}, format='json')
Expand All @@ -41,11 +46,8 @@ def test_login_creates_keys(self):

def test_logout_deletes_keys(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
for _ in range(2):
token = AuthToken.objects.create(user=user)
token = AuthToken.objects.create(user=self.user)
self.assertEqual(AuthToken.objects.count(), 2)

url = reverse('knox_logout')
Expand All @@ -55,11 +57,8 @@ def test_logout_deletes_keys(self):

def test_logout_all_deletes_keys(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
for _ in range(10):
token = AuthToken.objects.create(user=user)
token = AuthToken.objects.create(user=self.user)
self.assertEqual(AuthToken.objects.count(), 10)

url = reverse('knox_logoutall')
Expand All @@ -69,14 +68,9 @@ def test_logout_all_deletes_keys(self):

def test_logout_all_deletes_only_targets_keys(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
user2 = User.objects.create_user(
'user2', '[email protected]', password)
for _ in range(10):
token = AuthToken.objects.create(user=user)
token2 = AuthToken.objects.create(user=user2)
token = AuthToken.objects.create(user=self.user)
token2 = AuthToken.objects.create(user=self.user2)
self.assertEqual(AuthToken.objects.count(), 20)

url = reverse('knox_logoutall')
Expand All @@ -86,11 +80,8 @@ def test_logout_all_deletes_only_targets_keys(self):

def test_expired_tokens_login_fails(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
token = AuthToken.objects.create(
user=user, expires=datetime.timedelta(seconds=0))
user=self.user, expires=datetime.timedelta(seconds=0))
url = reverse('api-root')
self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
response = self.client.post(url, {}, format='json')
Expand All @@ -99,13 +90,10 @@ def test_expired_tokens_login_fails(self):

def test_expired_tokens_deleted(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
for _ in range(10):
# 0 TTL gives an expired token
token = AuthToken.objects.create(
user=user, expires=datetime.timedelta(seconds=0))
user=self.user, expires=datetime.timedelta(seconds=0))
self.assertEqual(AuthToken.objects.count(), 10)

# Attempting a single logout should delete all tokens
Expand All @@ -117,14 +105,11 @@ def test_expired_tokens_deleted(self):

def test_update_token_key(self):
self.assertEqual(AuthToken.objects.count(), 0)
username, password = 'root', 'toor'
user = User.objects.create_user(
username, '[email protected]', password)
token = AuthToken.objects.create(user)
token = AuthToken.objects.create(self.user)
rf = APIRequestFactory()
request = rf.get('/')
request.META = {'HTTP_AUTHORIZATION': 'Token {}'.format(token)}
(user, auth_token) = TokenAuthentication().authenticate(request)
(self.user, auth_token) = TokenAuthentication().authenticate(request)
self.assertEqual(
token[:CONSTANTS.TOKEN_KEY_LENGTH],
auth_token.token_key)
Expand All @@ -136,3 +121,12 @@ def test_invalid_token_length_returns_401_code(self):
response = self.client.post(url, {}, format='json')
self.assertEqual(response.status_code, 401)
self.assertEqual(response.data, {"detail": "Invalid token."})

def test_invalid_odd_length_token_returns_401_code(self):
token = AuthToken.objects.create(self.user)
odd_length_token = token + '1'
url = reverse('api-root')
self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % odd_length_token))
response = self.client.post(url, {}, format='json')
self.assertEqual(response.status_code, 401)
self.assertEqual(response.data, {"detail": "Invalid token."})

0 comments on commit 07d8605

Please sign in to comment.