Repository: jazzband/djangorestframework-simplejwt (language: python)

Issue:
OutstandingToken user should be nullified with SET_NULL
Fixes #266 #232 #201 

Originally, in the model of OutstandingToken, you can see that the user attribute is supposed to be null if the user is deleted. If it remained `on_delete=models.CASCADE` then there would be no point in `null=True` and `blank=True`. I think the implementation was just unintended behavior, so the cascading on user deletion should set the OutstandingToken's user to null.


Discussion / hints:

Fix patch:
diff --git a/rest_framework_simplejwt/token_blacklist/migrations/0012_alter_outstandingtoken_user.py b/rest_framework_simplejwt/token_blacklist/migrations/0012_alter_outstandingtoken_user.py
new file mode 100644
index 000000000..652f06b69
--- /dev/null
+++ b/rest_framework_simplejwt/token_blacklist/migrations/0012_alter_outstandingtoken_user.py
@@ -0,0 +1,26 @@
+# Generated by Django 3.2.10 on 2022-01-24 06:42
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ("token_blacklist", "0011_linearizes_history"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="outstandingtoken",
+            name="user",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to=settings.AUTH_USER_MODEL,
+            ),
+        ),
+    ]
diff --git a/rest_framework_simplejwt/token_blacklist/models.py b/rest_framework_simplejwt/token_blacklist/models.py
index b646cd23a..c84b8c4ae 100644
--- a/rest_framework_simplejwt/token_blacklist/models.py
+++ b/rest_framework_simplejwt/token_blacklist/models.py
@@ -5,7 +5,7 @@
 class OutstandingToken(models.Model):
     id = models.BigAutoField(primary_key=True, serialize=False)
     user = models.ForeignKey(
-        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True
+        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True
     )
 
     jti = models.CharField(unique=True, max_length=255)


Test patch:
diff --git a/tests/test_token_blacklist.py b/tests/test_token_blacklist.py
index 8286c936b..67ea7fada 100644
--- a/tests/test_token_blacklist.py
+++ b/tests/test_token_blacklist.py
@@ -172,6 +172,29 @@ def test_it_should_delete_any_expired_tokens(self):
             [not_expired_2["jti"], not_expired_3["jti"]],
         )
 
+    def test_token_blacklist_will_not_be_removed_on_User_delete(self):
+        token = RefreshToken.for_user(self.user)
+        outstanding_token = OutstandingToken.objects.first()
+
+        # Should raise no exception
+        RefreshToken(str(token))
+
+        # Add token to blacklist
+        BlacklistedToken.objects.create(token=outstanding_token)
+
+        with self.assertRaises(TokenError) as e:
+            # Should raise exception
+            RefreshToken(str(token))
+            self.assertIn("blacklisted", e.exception.args[0])
+
+        # Delete the User and try again
+        self.user.delete()
+
+        with self.assertRaises(TokenError) as e:
+            # Should raise exception
+            RefreshToken(str(token))
+            self.assertIn("blacklisted", e.exception.args[0])
+
 
 class TestPopulateJtiHexMigration(MigrationTestCase):
     migrate_from = ("token_blacklist", "0002_outstandingtoken_jti_hex")