Skip to content

Commit f0e1d1f

Browse files
committed
An update to how tokens are generated, as well as a removal of the unique constraint to accomodate multiple false tokens of the same type.
1 parent e69b14b commit f0e1d1f

File tree

7 files changed

+49
-6
lines changed

7 files changed

+49
-6
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ DEFAULTS = {
320320
# configurable function for sending sms
321321
'PASSWORDLESS_SMS_CALLBACK': 'drfpasswordless.utils.send_sms_with_callback_token'
322322
323+
# Token Generation Retry Count
324+
'PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS': 3
325+
323326
324327
}
325328
```

drfpasswordless/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
__title__ = 'drfpasswordless'
4-
__version__ = '1.5.6'
4+
__version__ = '1.5.7'
55
__author__ = 'Aaron Ng'
66
__license__ = 'MIT'
77
__copyright__ = 'Copyright 2020 Aaron Ng'

drfpasswordless/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
VERSION = (1, 5, 6)
1+
VERSION = (1, 5, 7)
22

33
__version__ = '.'.join(map(str, VERSION))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 3.0.2 on 2020-11-17 04:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('drfpasswordless', '0004_auto_20200125_0853'),
10+
]
11+
12+
operations = [
13+
migrations.AlterUniqueTogether(
14+
name='callbacktoken',
15+
unique_together=set(),
16+
),
17+
]

drfpasswordless/models.py

-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class Meta:
4747
abstract = True
4848
get_latest_by = 'created_at'
4949
ordering = ['-id']
50-
unique_together = (('key', 'is_active'),)
5150

5251
def __str__(self):
5352
return str(self.key)
@@ -66,4 +65,3 @@ class CallbackToken(AbstractBaseCallbackToken):
6665

6766
class Meta(AbstractBaseCallbackToken.Meta):
6867
verbose_name = 'Callback Token'
69-
unique_together = ['is_active', 'key', 'type']

drfpasswordless/settings.py

+2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
'PASSWORDLESS_EMAIL_CALLBACK': 'drfpasswordless.utils.send_email_with_callback_token',
8989
'PASSWORDLESS_SMS_CALLBACK': 'drfpasswordless.utils.send_sms_with_callback_token',
9090

91+
# Token Generation Retry Count
92+
'PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS': 3
9193
}
9294

9395
# List of settings that may be in string import notation.

drfpasswordless/signals.py

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from django.contrib.auth import get_user_model
3+
from django.core.exceptions import ValidationError
34
from django.dispatch import receiver
45
from django.db.models import signals
56
from drfpasswordless.models import CallbackToken
@@ -32,13 +33,35 @@ def check_unique_tokens(sender, instance, **kwargs):
3233
# save is called on a token to create it in the db
3334
# before creating check whether a token with the same key exists
3435
if isinstance(instance, CallbackToken):
36+
# Try three times.
37+
unique = False
38+
tries = 0
39+
3540
if CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
36-
instance.key = generate_numeric_token()
41+
# Try N(default=3) times before giving up.
42+
while tries < api_settings.PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS:
43+
tries = tries + 1
44+
new_key = generate_numeric_token()
45+
instance.key = new_key
46+
47+
if not CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
48+
# Leave the loop if we found a valid token that doesn't exist yet.
49+
unique = True
50+
break
51+
52+
if not unique:
53+
# A unique value wasn't found after three tries
54+
raise ValidationError("Couldn't create a unique token even after retrying.")
55+
else:
56+
# A unique value was found immediately.
57+
pass
58+
59+
3760
else:
3861
# save is called on an already existing token to update it. Such as invalidating it.
3962
# in that case there is no need to check for the key. This way we both avoid an unneccessary db hit
4063
# and avoid to change key field of used tokens.
41-
pass
64+
pass
4265

4366

4467

0 commit comments

Comments
 (0)