|
1 | 1 | import logging
|
2 | 2 | from django.contrib.auth import get_user_model
|
| 3 | +from django.core.exceptions import ValidationError |
3 | 4 | from django.dispatch import receiver
|
4 | 5 | from django.db.models import signals
|
5 | 6 | from drfpasswordless.models import CallbackToken
|
@@ -27,18 +28,41 @@ def invalidate_previous_tokens(sender, instance, created, **kwargs):
|
27 | 28 | def check_unique_tokens(sender, instance, **kwargs):
|
28 | 29 | """
|
29 | 30 | Ensures that mobile and email tokens are unique or tries once more to generate.
|
| 31 | + Note that here we've decided keys are unique even across auth and validation. |
| 32 | + We could consider relaxing this in the future as well by filtering on the instance.type. |
30 | 33 | """
|
31 | 34 | if instance._state.adding:
|
32 | 35 | # save is called on a token to create it in the db
|
33 | 36 | # before creating check whether a token with the same key exists
|
34 | 37 | if isinstance(instance, CallbackToken):
|
| 38 | + unique = False |
| 39 | + tries = 0 |
| 40 | + |
35 | 41 | if CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
|
36 |
| - instance.key = generate_numeric_token() |
| 42 | + # Try N(default=3) times before giving up. |
| 43 | + while tries < api_settings.PASSWORDLESS_TOKEN_GENERATION_ATTEMPTS: |
| 44 | + tries = tries + 1 |
| 45 | + new_key = generate_numeric_token() |
| 46 | + instance.key = new_key |
| 47 | + |
| 48 | + if not CallbackToken.objects.filter(key=instance.key, is_active=True).exists(): |
| 49 | + # Leave the loop if we found a valid token that doesn't exist yet. |
| 50 | + unique = True |
| 51 | + break |
| 52 | + |
| 53 | + if not unique: |
| 54 | + # A unique value wasn't found after three tries |
| 55 | + raise ValidationError("Couldn't create a unique token even after retrying.") |
| 56 | + else: |
| 57 | + # A unique value was found immediately. |
| 58 | + pass |
| 59 | + |
| 60 | + |
37 | 61 | else:
|
38 | 62 | # save is called on an already existing token to update it. Such as invalidating it.
|
39 | 63 | # in that case there is no need to check for the key. This way we both avoid an unneccessary db hit
|
40 | 64 | # and avoid to change key field of used tokens.
|
41 |
| - pass |
| 65 | + pass |
42 | 66 |
|
43 | 67 |
|
44 | 68 |
|
|
0 commit comments