Skip to content

Commit

Permalink
An update to how tokens are generated, as well as a removal of the un…
Browse files Browse the repository at this point in the history
…ique constraint to accomodate multiple false tokens of the same type.
  • Loading branch information
aaronn committed Nov 17, 2020
1 parent e69b14b commit f0e1d1f
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 6 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ DEFAULTS = {
# configurable function for sending sms
'PASSWORDLESS_SMS_CALLBACK': 'drfpasswordless.utils.send_sms_with_callback_token'
# Token Generation Retry Count
'PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS': 3
}
```
Expand Down
2 changes: 1 addition & 1 deletion drfpasswordless/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

__title__ = 'drfpasswordless'
__version__ = '1.5.6'
__version__ = '1.5.7'
__author__ = 'Aaron Ng'
__license__ = 'MIT'
__copyright__ = 'Copyright 2020 Aaron Ng'
Expand Down
2 changes: 1 addition & 1 deletion drfpasswordless/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VERSION = (1, 5, 6)
VERSION = (1, 5, 7)

__version__ = '.'.join(map(str, VERSION))
17 changes: 17 additions & 0 deletions drfpasswordless/migrations/0005_auto_20201117_0410.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 3.0.2 on 2020-11-17 04:10

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('drfpasswordless', '0004_auto_20200125_0853'),
]

operations = [
migrations.AlterUniqueTogether(
name='callbacktoken',
unique_together=set(),
),
]
2 changes: 0 additions & 2 deletions drfpasswordless/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class Meta:
abstract = True
get_latest_by = 'created_at'
ordering = ['-id']
unique_together = (('key', 'is_active'),)

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

class Meta(AbstractBaseCallbackToken.Meta):
verbose_name = 'Callback Token'
unique_together = ['is_active', 'key', 'type']
2 changes: 2 additions & 0 deletions drfpasswordless/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
'PASSWORDLESS_EMAIL_CALLBACK': 'drfpasswordless.utils.send_email_with_callback_token',
'PASSWORDLESS_SMS_CALLBACK': 'drfpasswordless.utils.send_sms_with_callback_token',

# Token Generation Retry Count
'PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS': 3
}

# List of settings that may be in string import notation.
Expand Down
27 changes: 25 additions & 2 deletions drfpasswordless/signals.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.dispatch import receiver
from django.db.models import signals
from drfpasswordless.models import CallbackToken
Expand Down Expand Up @@ -32,13 +33,35 @@ def check_unique_tokens(sender, instance, **kwargs):
# save is called on a token to create it in the db
# before creating check whether a token with the same key exists
if isinstance(instance, CallbackToken):
# Try three times.
unique = False
tries = 0

if CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
instance.key = generate_numeric_token()
# Try N(default=3) times before giving up.
while tries < api_settings.PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS:
tries = tries + 1
new_key = generate_numeric_token()
instance.key = new_key

if not CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
# Leave the loop if we found a valid token that doesn't exist yet.
unique = True
break

if not unique:
# A unique value wasn't found after three tries
raise ValidationError("Couldn't create a unique token even after retrying.")
else:
# A unique value was found immediately.
pass


else:
# save is called on an already existing token to update it. Such as invalidating it.
# in that case there is no need to check for the key. This way we both avoid an unneccessary db hit
# and avoid to change key field of used tokens.
pass
pass



Expand Down

0 comments on commit f0e1d1f

Please sign in to comment.