From 7d691c806e09a669b9283d6be1f7cd1302ea7f34 Mon Sep 17 00:00:00 2001 From: Philipp Siegmantel Date: Thu, 16 Apr 2020 10:05:08 +0200 Subject: [PATCH 1/4] add blacklist check on verfify endpoint --- rest_framework_simplejwt/serializers.py | 16 ++++++++++++++++ tests/test_token_blacklist.py | 11 +++++++++++ 2 files changed, 27 insertions(+) diff --git a/rest_framework_simplejwt/serializers.py b/rest_framework_simplejwt/serializers.py index c3fa58cf5..124a4c7e9 100644 --- a/rest_framework_simplejwt/serializers.py +++ b/rest_framework_simplejwt/serializers.py @@ -1,11 +1,16 @@ +from django.conf import settings from django.contrib.auth import authenticate from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, serializers +from rest_framework.exceptions import ValidationError +from .exceptions import TokenError from .settings import api_settings from .state import User from .tokens import RefreshToken, SlidingToken, UntypedToken +if 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS: + from .token_blacklist.models import BlacklistedToken class PasswordField(serializers.CharField): def __init__(self, *args, **kwargs): @@ -141,4 +146,15 @@ class TokenVerifySerializer(serializers.Serializer): def validate(self, attrs): UntypedToken(attrs['token']) + if 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS: + try: + token = RefreshToken(attrs['token']) + # not a refresh token + except TokenError: + return {} + + jti = token.get(api_settings.JTI_CLAIM) + if BlacklistedToken.objects.filter(token__jti=jti).exists(): + raise ValidationError(_('Token is blacklisted')) + return {} diff --git a/tests/test_token_blacklist.py b/tests/test_token_blacklist.py index b0de226a4..4917fdccd 100644 --- a/tests/test_token_blacklist.py +++ b/tests/test_token_blacklist.py @@ -5,6 +5,7 @@ from django.test import TestCase from rest_framework_simplejwt.exceptions import TokenError +from rest_framework_simplejwt.serializers import TokenVerifySerializer from rest_framework_simplejwt.settings import api_settings from rest_framework_simplejwt.token_blacklist.models import ( BlacklistedToken, OutstandingToken, @@ -190,3 +191,13 @@ def test_jti_field_should_contain_uuid_hex_strings(self): actual_hexes = [i.jti_hex for i in OutstandingToken.objects.all()] self.assertEqual(actual_hexes, self.expected_hexes) + + +class TokenVerifySerializerShouldHonourBlacklist(TestCase): + + def token_verify_serializer_shouldHonour_blacklist(self): + refresh_token = RefreshToken() + refresh_token.blacklist() + + serializer = TokenVerifySerializer({"token": refresh_token.payload}) + self.assertFalse(serializer.is_valid()) From c9c6c547d6ae8fcf2c86e03a5c1dbd327f98c47c Mon Sep 17 00:00:00 2001 From: Philipp Siegmantel Date: Thu, 9 Jul 2020 10:28:20 +0200 Subject: [PATCH 2/4] add review improvements --- rest_framework_simplejwt/serializers.py | 7 +++--- tests/test_token_blacklist.py | 31 ++++++++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/rest_framework_simplejwt/serializers.py b/rest_framework_simplejwt/serializers.py index 124a4c7e9..0b78e71db 100644 --- a/rest_framework_simplejwt/serializers.py +++ b/rest_framework_simplejwt/serializers.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.contrib.auth import authenticate from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, serializers @@ -9,7 +8,7 @@ from .state import User from .tokens import RefreshToken, SlidingToken, UntypedToken -if 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS: +if api_settings.BLACKLIST_AFTER_ROTATION: from .token_blacklist.models import BlacklistedToken class PasswordField(serializers.CharField): @@ -146,7 +145,7 @@ class TokenVerifySerializer(serializers.Serializer): def validate(self, attrs): UntypedToken(attrs['token']) - if 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS: + if api_settings.BLACKLIST_AFTER_ROTATION: try: token = RefreshToken(attrs['token']) # not a refresh token @@ -155,6 +154,6 @@ def validate(self, attrs): jti = token.get(api_settings.JTI_CLAIM) if BlacklistedToken.objects.filter(token__jti=jti).exists(): - raise ValidationError(_('Token is blacklisted')) + return {} return {} diff --git a/tests/test_token_blacklist.py b/tests/test_token_blacklist.py index 4917fdccd..f123ccc65 100644 --- a/tests/test_token_blacklist.py +++ b/tests/test_token_blacklist.py @@ -193,11 +193,30 @@ def test_jti_field_should_contain_uuid_hex_strings(self): self.assertEqual(actual_hexes, self.expected_hexes) -class TokenVerifySerializerShouldHonourBlacklist(TestCase): +class TokenVerifySerializerShouldHonourBlacklist(MigrationTestCase): + migrate_from = ('token_blacklist', '0002_outstandingtoken_jti_hex') + migrate_to = ('token_blacklist', '0003_auto_20171017_2007') + + def setUp(self): + self.user = User.objects.create( + username='test_user', + password='test_password', + ) + + super().setUp() + + def test_token_verify_serializer_should_honour_blacklist_if_blacklisting_enabled(self): + with self.settings(REST_FRAMEWORK={'BLACKLIST_AFTER_ROTATION': True}): + refresh_token = RefreshToken.for_user(self.user) + refresh_token.blacklist() + + serializer = TokenVerifySerializer(data={"token": str(refresh_token)}) + self.assertFalse(serializer.is_valid()) - def token_verify_serializer_shouldHonour_blacklist(self): - refresh_token = RefreshToken() - refresh_token.blacklist() + def test_token_verify_serializer_should_not_honour_blacklist_if_blacklisting_not_enabled(self): + with self.settings(REST_FRAMEWORK={'BLACKLIST_AFTER_ROTATION': False}): + refresh_token = RefreshToken.for_user(self.user) + refresh_token.blacklist() - serializer = TokenVerifySerializer({"token": refresh_token.payload}) - self.assertFalse(serializer.is_valid()) + serializer = TokenVerifySerializer(data={"token": str(refresh_token)}) + self.assertTrue(serializer.is_valid()) From b26c799bb20c43840887a5832ac0456f3b7160c0 Mon Sep 17 00:00:00 2001 From: Philipp Siegmantel Date: Fri, 10 Jul 2020 08:52:01 +0200 Subject: [PATCH 3/4] fix tests --- rest_framework_simplejwt/serializers.py | 10 ++-------- tests/test_token_blacklist.py | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/rest_framework_simplejwt/serializers.py b/rest_framework_simplejwt/serializers.py index 0b78e71db..39400c846 100644 --- a/rest_framework_simplejwt/serializers.py +++ b/rest_framework_simplejwt/serializers.py @@ -143,17 +143,11 @@ class TokenVerifySerializer(serializers.Serializer): token = serializers.CharField() def validate(self, attrs): - UntypedToken(attrs['token']) + token = UntypedToken(attrs['token']) if api_settings.BLACKLIST_AFTER_ROTATION: - try: - token = RefreshToken(attrs['token']) - # not a refresh token - except TokenError: - return {} - jti = token.get(api_settings.JTI_CLAIM) if BlacklistedToken.objects.filter(token__jti=jti).exists(): - return {} + raise ValidationError("Token is blacklisted") return {} diff --git a/tests/test_token_blacklist.py b/tests/test_token_blacklist.py index f123ccc65..a747e9fe5 100644 --- a/tests/test_token_blacklist.py +++ b/tests/test_token_blacklist.py @@ -15,7 +15,7 @@ ) from rest_framework_simplejwt.utils import aware_utcnow, datetime_from_epoch -from .utils import MigrationTestCase +from .utils import MigrationTestCase, override_api_settings class TestTokenBlacklist(TestCase): @@ -206,7 +206,7 @@ def setUp(self): super().setUp() def test_token_verify_serializer_should_honour_blacklist_if_blacklisting_enabled(self): - with self.settings(REST_FRAMEWORK={'BLACKLIST_AFTER_ROTATION': True}): + with override_api_settings(BLACKLIST_AFTER_ROTATION=True): refresh_token = RefreshToken.for_user(self.user) refresh_token.blacklist() @@ -214,7 +214,7 @@ def test_token_verify_serializer_should_honour_blacklist_if_blacklisting_enabled self.assertFalse(serializer.is_valid()) def test_token_verify_serializer_should_not_honour_blacklist_if_blacklisting_not_enabled(self): - with self.settings(REST_FRAMEWORK={'BLACKLIST_AFTER_ROTATION': False}): + with override_api_settings(BLACKLIST_AFTER_ROTATION=False): refresh_token = RefreshToken.for_user(self.user) refresh_token.blacklist() From ad26e6658954aa8d99af343ef451d23d7680114d Mon Sep 17 00:00:00 2001 From: Philipp Siegmantel Date: Fri, 11 Dec 2020 16:14:06 +0100 Subject: [PATCH 4/4] fix lint --- rest_framework_simplejwt/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework_simplejwt/serializers.py b/rest_framework_simplejwt/serializers.py index 783efbbe3..63a38b6f6 100644 --- a/rest_framework_simplejwt/serializers.py +++ b/rest_framework_simplejwt/serializers.py @@ -4,13 +4,13 @@ from rest_framework import exceptions, serializers from rest_framework.exceptions import ValidationError -from .exceptions import TokenError from .settings import api_settings from .tokens import RefreshToken, SlidingToken, UntypedToken if api_settings.BLACKLIST_AFTER_ROTATION: from .token_blacklist.models import BlacklistedToken + class PasswordField(serializers.CharField): def __init__(self, *args, **kwargs): kwargs.setdefault('style', {})