Skip to content

Commit

Permalink
Ensure old tokens continue to work
Browse files Browse the repository at this point in the history
  • Loading branch information
jmsmkn committed Aug 2, 2024
1 parent 1e379b4 commit b75c2bd
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __pycache__/
# Distribution / packaging
.Python
env/
.venv/
build/
develop-eggs/
dist/
Expand Down
17 changes: 13 additions & 4 deletions knox/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,24 @@ def authenticate_credentials(self, token):
'''
msg = _('Invalid token.')
token = token.decode("utf-8")

try:
digest = hash_token(token)
except (TypeError, binascii.Error):
raise exceptions.AuthenticationFailed(msg)

for auth_token in get_token_model().objects.filter(token_key=token[:8]):
# Migrate tokens that were created prior to 3a1bc58
# TODO: This will have terrible performance if TOKEN_PREFIX is used
if compare_digest(digest, auth_token.digest):
auth_token.token_key = token[:CONSTANTS.TOKEN_KEY_LENGTH]
auth_token.save()

for auth_token in get_token_model().objects.filter(
token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH]):
if self._cleanup_token(auth_token):
continue

try:
digest = hash_token(token)
except (TypeError, binascii.Error):
raise exceptions.AuthenticationFailed(msg)
if compare_digest(digest, auth_token.digest):
if knox_settings.AUTO_REFRESH and auth_token.expiry:
self.renew_token(auth_token)
Expand Down
18 changes: 18 additions & 0 deletions knox/migrations/0010_alter_authtoken_token_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.7 on 2024-08-02 08:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('knox', '0009_extend_authtoken_field'),
]

operations = [
migrations.AlterField(
model_name='authtoken',
name='token_key',
field=models.CharField(db_index=True, max_length=15),
),
]
5 changes: 1 addition & 4 deletions knox/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from knox import crypto
from knox.settings import CONSTANTS, knox_settings

sha = knox_settings.SECURE_HASH_ALGORITHM

User = settings.AUTH_USER_MODEL


Expand Down Expand Up @@ -37,8 +35,7 @@ class AbstractAuthToken(models.Model):
digest = models.CharField(
max_length=CONSTANTS.DIGEST_LENGTH, primary_key=True)
token_key = models.CharField(
max_length=CONSTANTS.MAXIMUM_TOKEN_PREFIX_LENGTH +
CONSTANTS.TOKEN_KEY_LENGTH,
max_length=CONSTANTS.TOKEN_KEY_LENGTH,
db_index=True
)
user = models.ForeignKey(User, null=False, blank=False,
Expand Down
27 changes: 27 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,30 @@ def test_tokens_created_before_prefix_still_work(self):
response = self.client.get(root_url, {}, format='json')
self.assertEqual(response.status_code, 200)
reload(views)

def test_old_tokens_still_work(self):
self.assertEqual(AuthToken.objects.count(), 0)

old_token = "02d233c901e7bd38df1dbc486b7e22c5c81b089c40cbb31d35d7b032615f5778"
# Hash generated using crypto.hash_token on 4.2.0 with
# SECURE_HASH_ALGORITHM = 'cryptography.hazmat.primitives.hashes.SHA512'
old_hash = (
"c7f9f2904decf77e0fa0341bc3eb96daa1437649825f4bfdd38cdad64d69c4be55938d71f17"
"34131c656f9bbbfc5d991bef295accd268921b23d9cdd0d9d60d0"
)

AuthToken(
token_key=old_token[: 8], # 8 was the key length prior to 3a1bc58
digest=old_hash,
user=self.user,
).save()

rf = APIRequestFactory()
request = rf.get('/')
request.META = {'HTTP_AUTHORIZATION': f'Token {old_token}'}
user, auth_token = TokenAuthentication().authenticate(request)
self.assertEqual(self.user, user)
self.assertEqual(
old_token[:CONSTANTS.TOKEN_KEY_LENGTH],
auth_token.token_key,
)
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ envlist =
[testenv]
commands =
python manage.py migrate
coverage run manage.py test
coverage run manage.py test {posargs}
coverage report
setenv =
DJANGO_SETTINGS_MODULE = knox_project.settings
Expand Down

0 comments on commit b75c2bd

Please sign in to comment.