From a6dc006bbc86faa4580a333af01503e809929135 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Jan 2020 00:35:53 -0800 Subject: [PATCH 01/40] New alias endpoints initial --- README.md | 20 ++++++------------- drfpasswordless/__init__.py | 2 +- drfpasswordless/__version__.py | 2 +- drfpasswordless/admin.py | 10 +++++----- .../migrations/0003_callbacktoken_type.py | 19 ++++++++++++++++++ drfpasswordless/models.py | 6 ++++++ drfpasswordless/services.py | 4 ++-- drfpasswordless/signals.py | 6 +++--- drfpasswordless/urls.py | 12 +++++------ drfpasswordless/utils.py | 20 ++++++++++--------- tests/test_authentication.py | 6 +++--- tests/test_settings.py | 4 ++-- tests/test_verification.py | 12 +++++------ 13 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 drfpasswordless/migrations/0003_callbacktoken_type.py diff --git a/README.md b/README.md index 79ba22c..887fb81 100644 --- a/README.md +++ b/README.md @@ -111,11 +111,11 @@ PASSWORDLESS_AUTH = { 5. You can now POST to either of the endpoints: ```bash -curl -X POST -d "email=aaron@email.com" localhost:8000/auth/email/ +curl -X POST -d "email=aaron@email.com" localhost:8000/auth/alias/ // OR -curl -X POST -d "mobile=+15552143912" localhost:8000/auth/mobile/ +curl -X POST -d "mobile=+15552143912" localhost:8000/auth/alias/ ``` A 6 digit callback token will be sent to the contact point. @@ -125,7 +125,7 @@ curl -X POST -d "mobile=+15552143912" localhost:8000/auth/mobile/ TokenAuthentication scheme. ```bash -curl -X POST -d "token=815381" localhost:8000/callback/auth/ +curl -X POST -d "email=aaron@email.com&token=815381" localhost:8000/auth/token/ > HTTP/1.0 200 OK > {"token":"76be2d9ecfaf5fa4226d722bzdd8a4fff207ed0e”} @@ -149,19 +149,11 @@ You’ll also need to set up an SMTP server to send emails (`See Django Docs `__), but for development you can set up a dummy development smtp server to test emails. Sent emails will print to the console. `Read more -here. `__ +here. `__ ```python # Settings.py -… -EMAIL_HOST = 'localhost' -EMAIL_PORT = 1025 -``` - -Then run the following: - -```bash -python -m smtpd -n -c DebuggingServer localhost:1025 +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' ``` Configuring Mobile @@ -338,7 +330,7 @@ License The MIT License (MIT) -Copyright (c) 2018 Aaron Ng +Copyright (c) 2020 Aaron Ng Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/drfpasswordless/__init__.py b/drfpasswordless/__init__.py index ecdfc2a..7c8b987 100644 --- a/drfpasswordless/__init__.py +++ b/drfpasswordless/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- __title__ = 'drfpasswordless' -__version__ = '1.4.0' +__version__ = '1.5.0' __author__ = 'Aaron Ng' __license__ = 'MIT' __copyright__ = 'Copyright 2020 Aaron Ng' diff --git a/drfpasswordless/__version__.py b/drfpasswordless/__version__.py index d9e5b37..d7ecfd8 100644 --- a/drfpasswordless/__version__.py +++ b/drfpasswordless/__version__.py @@ -1,3 +1,3 @@ -VERSION = (1, 4, 0) +VERSION = (1, 5, 0) __version__ = '.'.join(map(str, VERSION)) diff --git a/drfpasswordless/admin.py b/drfpasswordless/admin.py index d983d07..268d699 100644 --- a/drfpasswordless/admin.py +++ b/drfpasswordless/admin.py @@ -19,8 +19,8 @@ def link_to_user(self, obj): class AbstractCallbackTokenInline(admin.StackedInline): max_num = 0 extra = 0 - readonly_fields = ('created_at', 'key', 'is_active') - fields = ('created_at', 'user', 'key', 'is_active') + readonly_fields = ('created_at', 'key', 'type', 'is_active') + fields = ('created_at', 'user', 'key', 'type', 'is_active') class CallbackInline(AbstractCallbackTokenInline): @@ -28,7 +28,7 @@ class CallbackInline(AbstractCallbackTokenInline): class AbstractCallbackTokenAdmin(UserLinkMixin, admin.ModelAdmin): - readonly_fields = ('created_at', 'user', 'key') - list_display = ('created_at', UserLinkMixin.LINK_TO_USER_FIELD, 'key', 'is_active') - fields = ('created_at', 'user', 'key', 'is_active') + readonly_fields = ('created_at', 'user', 'key', 'type',) + list_display = ('created_at', UserLinkMixin.LINK_TO_USER_FIELD, 'key', 'type', 'is_active') + fields = ('created_at', 'user', 'key', 'type', 'is_active') extra = 0 diff --git a/drfpasswordless/migrations/0003_callbacktoken_type.py b/drfpasswordless/migrations/0003_callbacktoken_type.py new file mode 100644 index 0000000..2160ae2 --- /dev/null +++ b/drfpasswordless/migrations/0003_callbacktoken_type.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.2 on 2020-01-22 08:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('drfpasswordless', '0002_auto_20200122_0424'), + ] + + operations = [ + migrations.AddField( + model_name='callbacktoken', + name='type', + field=models.CharField(choices=[('AUTH', 'Auth'), ('VERIFY', 'Verify')], default='VERIFY', max_length=20), + preserve_default=False, + ), + ] diff --git a/drfpasswordless/models.py b/drfpasswordless/models.py index f657b16..bdac4ba 100644 --- a/drfpasswordless/models.py +++ b/drfpasswordless/models.py @@ -33,6 +33,7 @@ class AbstractBaseCallbackToken(models.Model): When a new token is created, older ones of the same type are invalidated via the pre_save signal in signals.py. """ + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True) created_at = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name=None, on_delete=models.CASCADE) @@ -56,7 +57,12 @@ class CallbackToken(AbstractBaseCallbackToken): """ Generates a random six digit number to be returned. """ + TOKEN_TYPE_AUTH = 'AUTH' + TOKEN_TYPE_VERIFY = 'VERIFY' + TOKEN_TYPES = ((TOKEN_TYPE_AUTH, 'Auth'), (TOKEN_TYPE_VERIFY, 'Verify')) + key = models.CharField(default=generate_numeric_token, max_length=6, unique=True) + type = models.CharField(max_length=20, choices=TOKEN_TYPES) class Meta(AbstractBaseCallbackToken.Meta): verbose_name = 'Callback Token' diff --git a/drfpasswordless/services.py b/drfpasswordless/services.py index 42ca867..f165237 100644 --- a/drfpasswordless/services.py +++ b/drfpasswordless/services.py @@ -7,8 +7,8 @@ class TokenService(object): @staticmethod - def send_token(user, alias_type, **message_payload): - token = create_callback_token_for_user(user, alias_type) + def send_token(user, alias_type, token_type, **message_payload): + token = create_callback_token_for_user(user, alias_type, token_type) send_action = None if alias_type == 'email': send_action = send_email_with_callback_token diff --git a/drfpasswordless/signals.py b/drfpasswordless/signals.py index 7e02fd1..0558957 100644 --- a/drfpasswordless/signals.py +++ b/drfpasswordless/signals.py @@ -17,7 +17,7 @@ def invalidate_previous_tokens(sender, instance, **kwargs): """ active_tokens = None if isinstance(instance, CallbackToken): - active_tokens = CallbackToken.objects.active().filter(user=instance.user).exclude(id=instance.id) + active_tokens = CallbackToken.objects.active().filter(user=instance.user, type=instance.type).exclude(id=instance.id) # Invalidate tokens if active_tokens: @@ -72,7 +72,7 @@ def update_alias_verification(sender, instance, **kwargs): message_payload = {'email_subject': email_subject, 'email_plaintext': email_plaintext, 'email_html': email_html} - success = TokenService.send_token(instance, 'email', **message_payload) + success = TokenService.send_token(instance, 'email', CallbackToken.TOKEN_TYPE_VERIFY, **message_payload) if success: logger.info('drfpasswordless: Successfully sent email on updated address: %s' @@ -104,7 +104,7 @@ def update_alias_verification(sender, instance, **kwargs): if api_settings.PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN is True: mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE message_payload = {'mobile_message': mobile_message} - success = TokenService.send_token(instance, 'mobile', **message_payload) + success = TokenService.send_token(instance, 'mobile', CallbackToken.TOKEN_TYPE_VERIFY, **message_payload) if success: logger.info('drfpasswordless: Successfully sent SMS on updated mobile: %s' diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index 1fa8f69..ebc3963 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -9,10 +9,10 @@ ) urlpatterns = [ - path('callback/auth/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), - path('auth/email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), - path('auth/mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), - path('callback/verify/', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), - path('verify/email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), - path('verify/mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), + path('auth/email/', ObtainEmailCallbackToken.as_view(), name='auth_alias'), + path('auth/mobile/', ObtainMobileCallbackToken.as_view(), name='auth_alias'), + path('auth/token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), + path('auth/verify/email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), + path('auth/verify/mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), + path('auth/verify/', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), ] diff --git a/drfpasswordless/utils.py b/drfpasswordless/utils.py index d1e73ed..2bdc106 100644 --- a/drfpasswordless/utils.py +++ b/drfpasswordless/utils.py @@ -16,7 +16,7 @@ def authenticate_by_token(callback_token): try: - token = CallbackToken.objects.get(key=callback_token, is_active=True) + token = CallbackToken.objects.get(key=callback_token, is_active=True, type=CallbackToken.TOKEN_TYPE_AUTH) # Returning a user designates a successful authentication. token.user = User.objects.get(pk=token.user.pk) @@ -35,20 +35,22 @@ def authenticate_by_token(callback_token): return None -def create_callback_token_for_user(user, token_type): +def create_callback_token_for_user(user, alias_type, token_type): token = None - token_type = token_type.upper() + alias_type_u = alias_type.upper() - if token_type == 'EMAIL': + if alias_type_u == 'EMAIL': token = CallbackToken.objects.create(user=user, - to_alias_type=token_type, - to_alias=getattr(user, api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME)) + to_alias_type=alias_type_u, + to_alias=getattr(user, api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME), + type=token_type) - elif token_type == 'MOBILE': + elif alias_type_u == 'MOBILE': token = CallbackToken.objects.create(user=user, - to_alias_type=token_type, - to_alias=getattr(user, api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME)) + to_alias_type=alias_type_u, + to_alias=getattr(user, api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME), + type=token_type) if token is not None: return token diff --git a/tests/test_authentication.py b/tests/test_authentication.py index ecda58f..b617810 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -76,7 +76,7 @@ def setUp(self): self.email = 'aaron@example.com' self.url = '/auth/email/' - self.challenge_url = '/callback/auth/' + self.challenge_url = '/auth/token/' self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.user = User.objects.create(**{self.email_field_name: self.email}) @@ -228,7 +228,7 @@ def setUp(self): self.email = 'aaron@example.com' self.url = '/auth/email/' - self.challenge_url = '/callback/auth/' + self.challenge_url = '/auth/token/' self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.user = User.objects.create(**{self.email_field_name: self.email}) @@ -269,7 +269,7 @@ def setUp(self): self.mobile = '+15551234567' self.url = '/auth/mobile/' - self.challenge_url = '/callback/auth/' + self.challenge_url = '/auth/token/' self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME diff --git a/tests/test_settings.py b/tests/test_settings.py index f62524f..1f5ec38 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -90,7 +90,7 @@ def setUp(self): api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED = True self.url = '/auth/email/' - self.callback_url = '/callback/auth/' + self.callback_url = '/auth/token/' self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.email_verified_field_name = api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME @@ -140,7 +140,7 @@ def setUp(self): api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED = True self.url = '/auth/mobile/' - self.callback_url = '/callback/auth/' + self.callback_url = '/auth/token/' self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME self.mobile_verified_field_name = api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME diff --git a/tests/test_verification.py b/tests/test_verification.py index 37873ed..3e25b40 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -16,9 +16,9 @@ def setUp(self): api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED = True self.url = '/auth/email/' - self.callback_url = '/callback/auth/' - self.verify_url = '/verify/email/' - self.callback_verify = '/callback/verify/' + self.callback_url = '/auth/token/' + self.verify_url = '/auth/verify/email/' + self.callback_verify = '/auth/verify/' self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.email_verified_field_name = api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME @@ -91,9 +91,9 @@ def setUp(self): api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED = True self.url = '/auth/mobile/' - self.callback_url = '/callback/auth/' - self.verify_url = '/verify/mobile/' - self.callback_verify = '/callback/verify/' + self.callback_url = '/auth/token/' + self.verify_url = '/auth/verify/mobile/' + self.callback_verify = '/auth/verify/' self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME self.mobile_verified_field_name = api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME From 20146f2206200a89269a5ec0a179c2d0693b4c0f Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Jan 2020 00:43:35 -0800 Subject: [PATCH 02/40] Fix send_token calls --- README.md | 8 ++++---- drfpasswordless/views.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 887fb81..67e3d19 100644 --- a/README.md +++ b/README.md @@ -111,11 +111,11 @@ PASSWORDLESS_AUTH = { 5. You can now POST to either of the endpoints: ```bash -curl -X POST -d "email=aaron@email.com" localhost:8000/auth/alias/ +curl -X POST -d "email=aaron@email.com" localhost:8000/auth/email/ // OR -curl -X POST -d "mobile=+15552143912" localhost:8000/auth/alias/ +curl -X POST -d "mobile=+15552143912" localhost:8000/auth/mobile/ ``` A 6 digit callback token will be sent to the contact point. @@ -202,11 +202,11 @@ This is off by default but can be turned on with enabled they look for the User model fields ``email_verified`` or ``mobile_verified``. -You can also use ``/validate/email/`` or ``/validate/mobile/`` which will +You can also use ``auth/verify/email/`` or ``/auth/verify/mobile/`` which will automatically send a token to the endpoint attached to the current ``request.user``'s email or mobile if available. -You can then send that token to ``/callback/verify/`` which will double-check +You can then send that token to ``/auth/verify/`` which will double-check that the endpoint belongs to the request.user and mark the alias as verified. Registration diff --git a/drfpasswordless/views.py b/drfpasswordless/views.py index fa38396..c0d6611 100644 --- a/drfpasswordless/views.py +++ b/drfpasswordless/views.py @@ -4,6 +4,7 @@ from rest_framework.response import Response from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.views import APIView +from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.serializers import ( EmailAuthSerializer, @@ -37,6 +38,11 @@ def alias_type(self): # Alias Type raise NotImplementedError + @property + def token_type(self): + # Token Type + raise NotImplementedError + def post(self, request, *args, **kwargs): if self.alias_type.upper() not in api_settings.PASSWORDLESS_AUTH_TYPES: # Only allow auth types allowed in settings. @@ -68,6 +74,7 @@ class ObtainEmailCallbackToken(AbstractBaseObtainCallbackToken): failure_response = "Unable to email you a login code. Try again later." alias_type = 'email' + token_type = CallbackToken.TOKEN_TYPE_AUTH email_subject = api_settings.PASSWORDLESS_EMAIL_SUBJECT email_plaintext = api_settings.PASSWORDLESS_EMAIL_PLAINTEXT_MESSAGE @@ -84,6 +91,7 @@ class ObtainMobileCallbackToken(AbstractBaseObtainCallbackToken): failure_response = "Unable to send you a login code. Try again later." alias_type = 'mobile' + token_type = CallbackToken.TOKEN_TYPE_AUTH mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE message_payload = {'mobile_message': mobile_message} @@ -96,6 +104,7 @@ class ObtainEmailVerificationCallbackToken(AbstractBaseObtainCallbackToken): failure_response = "Unable to email you a verification code. Try again later." alias_type = 'email' + token_type = CallbackToken.TOKEN_TYPE_VERIFY email_subject = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT email_plaintext = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE @@ -114,6 +123,7 @@ class ObtainMobileVerificationCallbackToken(AbstractBaseObtainCallbackToken): failure_response = "Unable to send you a verification code. Try again later." alias_type = 'mobile' + token_type = CallbackToken.TOKEN_TYPE_VERIFY mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE message_payload = {'mobile_message': mobile_message} From e53fc5eb7892b8ac92ce49dda5a10f4b6d756f3e Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Jan 2020 00:46:08 -0800 Subject: [PATCH 03/40] Include token_type in send_token --- drfpasswordless/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drfpasswordless/views.py b/drfpasswordless/views.py index c0d6611..500cfbb 100644 --- a/drfpasswordless/views.py +++ b/drfpasswordless/views.py @@ -53,7 +53,7 @@ def post(self, request, *args, **kwargs): # Validate - user = serializer.validated_data['user'] # Create and send callback token - success = TokenService.send_token(user, self.alias_type, **self.message_payload) + success = TokenService.send_token(user, self.alias_type, self.token_type, **self.message_payload) # Respond With Success Or Failure of Sent if success: From 2f2f089a14d71afe7ad237711f9218aeb6699a29 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Jan 2020 00:50:54 -0800 Subject: [PATCH 04/40] Fix urls --- tests/urls.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/urls.py b/tests/urls.py index f7020d1..9b1c457 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -7,11 +7,11 @@ ObtainEmailVerificationCallbackToken, ObtainMobileVerificationCallbackToken, ) -urlpatterns = [url(r'^callback/auth/$', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), +urlpatterns = [url(r'^auth/token/$', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), url(r'^auth/email/$', ObtainEmailCallbackToken.as_view(), name='auth_email'), url(r'^auth/mobile/$', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), - url(r'^callback/verify/$', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), - url(r'^verify/email/$', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), - url(r'^verify/mobile/$', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile')] + url(r'^auth/verify/$', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), + url(r'^auth/verify/email/$', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), + url(r'^auth/verify/mobile/$', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile')] format_suffix_patterns(urlpatterns) From 5775f5e3fa2a7b7c7e828a01b49141731fdc17b1 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Jan 2020 01:13:57 -0800 Subject: [PATCH 05/40] Fix #30 infinite recursion on save signal --- drfpasswordless/signals.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drfpasswordless/signals.py b/drfpasswordless/signals.py index 0558957..de68979 100644 --- a/drfpasswordless/signals.py +++ b/drfpasswordless/signals.py @@ -10,20 +10,13 @@ logger = logging.getLogger(__name__) -@receiver(signals.pre_save, sender=CallbackToken) -def invalidate_previous_tokens(sender, instance, **kwargs): +@receiver(signals.post_save, sender=CallbackToken) +def invalidate_previous_tokens(sender, instance, created, **kwargs): """ - Invalidates all previously issued tokens as a post_save signal. + Invalidates all previously issued tokens of that type when a new one is created, used, or anything like that. """ - active_tokens = None if isinstance(instance, CallbackToken): - active_tokens = CallbackToken.objects.active().filter(user=instance.user, type=instance.type).exclude(id=instance.id) - - # Invalidate tokens - if active_tokens: - for token in active_tokens: - token.is_active = False - token.save() + CallbackToken.objects.active().filter(user=instance.user, type=instance.type).exclude(id=instance.id).update(is_active=False) @receiver(signals.pre_save, sender=CallbackToken) From bb18e0dd0d567f16f7ad23c201cf0bdffb02bf8b Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:08:48 -0800 Subject: [PATCH 06/40] Customizable URL paths --- README.md | 6 ++++++ drfpasswordless/settings.py | 6 ++++++ drfpasswordless/urls.py | 13 +++++++------ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 67e3d19..ef2fc61 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,12 @@ DEFAULTS = { # Allowed auth types, can be EMAIL, MOBILE, or both. 'PASSWORDLESS_AUTH_TYPES': ['EMAIL'], + # URL Prefix for Authentication Endpoints + 'PASSWORDLESS_AUTH_PREFIX': 'auth', + + # URL Prefix for Verification Endpoints + 'PASSWORDLESS_VERIFY_PREFIX': 'auth', + # Amount of time that tokens last, in seconds 'PASSWORDLESS_TOKEN_EXPIRE_TIME': 15 * 60, diff --git a/drfpasswordless/settings.py b/drfpasswordless/settings.py index 24af854..cc33c27 100644 --- a/drfpasswordless/settings.py +++ b/drfpasswordless/settings.py @@ -8,6 +8,12 @@ # Allowed auth types, can be EMAIL, MOBILE, or both. 'PASSWORDLESS_AUTH_TYPES': ['EMAIL'], + # URL Prefix for Authentication Endpoints + 'PASSWORDLESS_AUTH_PREFIX': 'auth/', + + # URL Prefix for Verification Endpoints + 'PASSWORDLESS_VERIFY_PREFIX': 'auth/verify/', + # Amount of time that tokens last, in seconds 'PASSWORDLESS_TOKEN_EXPIRE_TIME': 15 * 60, diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index ebc3963..ded236f 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -1,3 +1,4 @@ +from drfpasswordless.settings import api_settings from django.urls import path from drfpasswordless.views import ( ObtainEmailCallbackToken, @@ -9,10 +10,10 @@ ) urlpatterns = [ - path('auth/email/', ObtainEmailCallbackToken.as_view(), name='auth_alias'), - path('auth/mobile/', ObtainMobileCallbackToken.as_view(), name='auth_alias'), - path('auth/token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), - path('auth/verify/email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), - path('auth/verify/mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), - path('auth/verify/', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_alias'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_alias'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), ] From 0426d399506eed3a6a18950f6421949131c572fc Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:32:10 -0800 Subject: [PATCH 07/40] Tests for custom urls --- drfpasswordless/urls.py | 8 ++++---- tests/test_authentication.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index ded236f..5382566 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -10,10 +10,10 @@ ) urlpatterns = [ - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_alias'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_alias'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token'), path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token'), ] diff --git a/tests/test_authentication.py b/tests/test_authentication.py index b617810..8856cc9 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -3,6 +3,7 @@ from rest_framework.test import APITestCase from django.contrib.auth import get_user_model +from django.urls import reverse from drfpasswordless.settings import api_settings, DEFAULTS from drfpasswordless.utils import CallbackToken @@ -15,7 +16,7 @@ def setUp(self): api_settings.PASSWORDLESS_EMAIL_NOREPLY_ADDRESS = 'noreply@example.com' self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME - self.url = '/auth/email/' + self.url = reverse('drfpasswordless:auth_email') def test_email_signup_failed(self): email = 'failedemail182+' @@ -75,8 +76,8 @@ def setUp(self): api_settings.PASSWORDLESS_EMAIL_NOREPLY_ADDRESS = 'noreply@example.com' self.email = 'aaron@example.com' - self.url = '/auth/email/' - self.challenge_url = '/auth/token/' + self.url = reverse('drfpasswordless:auth_email') + self.challenge_url = reverse('drfpasswordless:auth_token') self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.user = User.objects.create(**{self.email_field_name: self.email}) @@ -156,7 +157,7 @@ def setUp(self): api_settings.PASSWORDLESS_TEST_SUPPRESSION = True api_settings.PASSWORDLESS_AUTH_TYPES = ['MOBILE'] api_settings.PASSWORDLESS_MOBILE_NOREPLY_NUMBER = '+15550000000' - self.url = '/auth/mobile/' + self.url = reverse('drfpasswordless:auth_mobile') self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME @@ -227,8 +228,8 @@ def setUp(self): api_settings.PASSWORDLESS_EMAIL_NOREPLY_ADDRESS = 'noreply@example.com' self.email = 'aaron@example.com' - self.url = '/auth/email/' - self.challenge_url = '/auth/token/' + self.url = reverse('drfpasswordless:auth_email') + self.challenge_url = reverse('drfpasswordless:auth_token') self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.user = User.objects.create(**{self.email_field_name: self.email}) @@ -268,8 +269,8 @@ def setUp(self): api_settings.PASSWORDLESS_MOBILE_NOREPLY_NUMBER = '+15550000000' self.mobile = '+15551234567' - self.url = '/auth/mobile/' - self.challenge_url = '/auth/token/' + self.url = reverse('drfpasswordless:auth_mobile') + self.challenge_url = reverse('drfpasswordless:auth_token') self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME From fdb67705d5990cd2da4be57c76cd0dbbb46a85a8 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:33:45 -0800 Subject: [PATCH 08/40] Fix tests for urls --- tests/urls.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/urls.py b/tests/urls.py index 9b1c457..96f0d6d 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,5 +1,6 @@ -from django.conf.urls import url +from django.urls import path from rest_framework.urlpatterns import format_suffix_patterns +from drfpasswordless.settings import api_settings from drfpasswordless.views import (ObtainEmailCallbackToken, ObtainMobileCallbackToken, ObtainAuthTokenFromCallbackToken, @@ -7,11 +8,16 @@ ObtainEmailVerificationCallbackToken, ObtainMobileVerificationCallbackToken, ) -urlpatterns = [url(r'^auth/token/$', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_callback'), - url(r'^auth/email/$', ObtainEmailCallbackToken.as_view(), name='auth_email'), - url(r'^auth/mobile/$', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), - url(r'^auth/verify/$', VerifyAliasFromCallbackToken.as_view(), name='verify_callback'), - url(r'^auth/verify/email/$', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), - url(r'^auth/verify/mobile/$', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile')] +urlpatterns = [ + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), + name='auth_token'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), + name='verify_email'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), + name='verify_mobile'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token'), +] format_suffix_patterns(urlpatterns) From e69cbc602d7c4efa82e7a6cf4fbff915425588f4 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:40:17 -0800 Subject: [PATCH 09/40] Fix tests for urls --- drfpasswordless/urls.py | 1 + tests/urls.py | 1 + 2 files changed, 2 insertions(+) diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index 5382566..85a33d1 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -9,6 +9,7 @@ ObtainMobileVerificationCallbackToken, ) +app_name = 'drfpasswordless' urlpatterns = [ path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), diff --git a/tests/urls.py b/tests/urls.py index 96f0d6d..cb77824 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -8,6 +8,7 @@ ObtainEmailVerificationCallbackToken, ObtainMobileVerificationCallbackToken, ) +app_name = 'drfpasswordless' urlpatterns = [ path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), From 8750a9f23fc9322854fc56a721bdcb181e5afd80 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:43:03 -0800 Subject: [PATCH 10/40] Try namespacing fix url tests --- drfpasswordless/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index 85a33d1..b27ad3a 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -10,6 +10,8 @@ ) app_name = 'drfpasswordless' +namespace = 'drfpasswordless' + urlpatterns = [ path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), From e59fc4851eb1c12c45d0b2e605362e6be086345c Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:45:08 -0800 Subject: [PATCH 11/40] Fix tests --- tests/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/urls.py b/tests/urls.py index cb77824..73867a4 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -9,6 +9,8 @@ ObtainMobileVerificationCallbackToken, ) app_name = 'drfpasswordless' +namespace = 'drfpasswordless' + urlpatterns = [ path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), From 4f0f52eb20d48e1d6e5dbc4016ae6dc4e4b61771 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:47:17 -0800 Subject: [PATCH 12/40] Namespace in urls --- drfpasswordless/urls.py | 13 ++++++------- tests/urls.py | 17 ++++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index b27ad3a..ee1995e 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -10,13 +10,12 @@ ) app_name = 'drfpasswordless' -namespace = 'drfpasswordless' urlpatterns = [ - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token', namespace='drfpasswordless'), ] diff --git a/tests/urls.py b/tests/urls.py index 73867a4..06af722 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -9,18 +9,13 @@ ObtainMobileVerificationCallbackToken, ) app_name = 'drfpasswordless' -namespace = 'drfpasswordless' - urlpatterns = [ - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), - name='auth_token'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), - name='verify_email'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), - name='verify_mobile'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token', namespace='drfpasswordless'), ] format_suffix_patterns(urlpatterns) From bb482b955794882cf970df73be64711e4b606437 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 23 Jan 2020 23:58:04 -0800 Subject: [PATCH 13/40] urls --- drfpasswordless/urls.py | 12 ++++++------ tests/test_settings.py | 13 +++++++------ tests/test_verification.py | 17 +++++++++-------- tests/urls.py | 10 +++------- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/drfpasswordless/urls.py b/drfpasswordless/urls.py index ee1995e..08189d7 100644 --- a/drfpasswordless/urls.py +++ b/drfpasswordless/urls.py @@ -12,10 +12,10 @@ app_name = 'drfpasswordless' urlpatterns = [ - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token', namespace='drfpasswordless'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile'), + path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile'), + path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token'), ] diff --git a/tests/test_settings.py b/tests/test_settings.py index 1f5ec38..f016d55 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -3,6 +3,7 @@ from rest_framework.test import APITestCase from django.contrib.auth import get_user_model +from django.urls import reverse from drfpasswordless.settings import api_settings, DEFAULTS from drfpasswordless.utils import CallbackToken @@ -19,11 +20,11 @@ def setUp(self): api_settings.PASSWORDLESS_TEST_SUPPRESSION = True self.email = 'aaron@example.com' - self.email_url = '/auth/email/' + self.email_url = reverse('drfpasswordless:auth_email') self.email_data = {'email': self.email} self.mobile = '+15551234567' - self.mobile_url = '/auth/mobile/' + self.mobile_url = reverse('drfpasswordless:auth_mobile') self.mobile_data = {'mobile': self.mobile} def test_email_auth_disabled(self): @@ -89,8 +90,8 @@ def setUp(self): api_settings.PASSWORDLESS_EMAIL_NOREPLY_ADDRESS = 'noreply@example.com' api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED = True - self.url = '/auth/email/' - self.callback_url = '/auth/token/' + self.url = reverse('drfpasswordless:auth_email') + self.callback_url = reverse('drfpasswordless:auth_token') self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.email_verified_field_name = api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME @@ -139,8 +140,8 @@ def setUp(self): api_settings.PASSWORDLESS_MOBILE_NOREPLY_NUMBER = '+15550000000' api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED = True - self.url = '/auth/mobile/' - self.callback_url = '/auth/token/' + self.url = reverse('drfpasswordless:auth_mobile') + self.callback_url = reverse('drfpasswordless:auth_token') self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME self.mobile_verified_field_name = api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME diff --git a/tests/test_verification.py b/tests/test_verification.py index 3e25b40..39f370b 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -2,6 +2,7 @@ from rest_framework.authtoken.models import Token from rest_framework.test import APITestCase from django.contrib.auth import get_user_model +from django.urls import reverse from drfpasswordless.settings import api_settings, DEFAULTS from drfpasswordless.utils import CallbackToken @@ -15,10 +16,10 @@ def setUp(self): api_settings.PASSWORDLESS_EMAIL_NOREPLY_ADDRESS = 'noreply@example.com' api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED = True - self.url = '/auth/email/' - self.callback_url = '/auth/token/' - self.verify_url = '/auth/verify/email/' - self.callback_verify = '/auth/verify/' + self.url = reverse('drfpasswordless:auth_email') + self.callback_url = reverse('drfpasswordless:auth_token') + self.verify_url = reverse('drfpasswordless:verify_email') + self.callback_verify = reverse('drfpasswordless:verify_token') self.email_field_name = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME self.email_verified_field_name = api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME @@ -90,10 +91,10 @@ def setUp(self): api_settings.PASSWORDLESS_MOBILE_NOREPLY_NUMBER = '+15550000000' api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED = True - self.url = '/auth/mobile/' - self.callback_url = '/auth/token/' - self.verify_url = '/auth/verify/mobile/' - self.callback_verify = '/auth/verify/' + self.url = reverse('drfpasswordless:auth_mobile') + self.callback_url = reverse('drfpasswordless:auth_token') + self.verify_url = reverse('drfpasswordless:verify_mobile') + self.callback_verify = reverse('drfpasswordless:verify_token') self.mobile_field_name = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME self.mobile_verified_field_name = api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME diff --git a/tests/urls.py b/tests/urls.py index 06af722..120fea0 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,4 +1,4 @@ -from django.urls import path +from django.urls import path, include from rest_framework.urlpatterns import format_suffix_patterns from drfpasswordless.settings import api_settings from drfpasswordless.views import (ObtainEmailCallbackToken, @@ -9,13 +9,9 @@ ObtainMobileVerificationCallbackToken, ) app_name = 'drfpasswordless' + urlpatterns = [ - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'email/', ObtainEmailCallbackToken.as_view(), name='auth_email', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'mobile/', ObtainMobileCallbackToken.as_view(), name='auth_mobile', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_AUTH_PREFIX + 'token/', ObtainAuthTokenFromCallbackToken.as_view(), name='auth_token', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'email/', ObtainEmailVerificationCallbackToken.as_view(), name='verify_email', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX + 'mobile/', ObtainMobileVerificationCallbackToken.as_view(), name='verify_mobile', namespace='drfpasswordless'), - path(api_settings.PASSWORDLESS_VERIFY_PREFIX, VerifyAliasFromCallbackToken.as_view(), name='verify_token', namespace='drfpasswordless'), + path('', include('drfpasswordless.urls')), ] format_suffix_patterns(urlpatterns) From 48c5d2794633398a0296a17053767e2f8c1bb25f Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:01:42 -0800 Subject: [PATCH 14/40] Add generic token and verify endpoints --- drfpasswordless/serializers.py | 95 +++++++++++++++++++++++----------- tests/test_authentication.py | 38 +++++++++++--- tests/test_verification.py | 8 +-- 3 files changed, 101 insertions(+), 40 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index eb6e9c1..1271fe8 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -168,43 +168,75 @@ class AbstractBaseCallbackTokenSerializer(serializers.Serializer): Abstract class inspired by DRF's own token serializer. Returns a user if valid, None or a message if not. """ + phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', + message="Mobile number must be entered in the format:" + " '+999999999'. Up to 15 digits allowed.") + + email = serializers.EmailField(required=False) # Needs to be required=false to require both. + mobile = serializers.CharField(required=False, validators=[phone_regex], max_length=15) token = TokenField(min_length=6, max_length=6, validators=[token_age_validator]) + def validate_alias(self, attrs): + email = attrs.get('email', None) + mobile = attrs.get('mobile', None) -class CallbackTokenAuthSerializer(AbstractBaseCallbackTokenSerializer): + if email and mobile: + raise serializers.ValidationError() - def validate(self, attrs): - callback_token = attrs.get('token', None) + if not email and not mobile: + raise serializers.ValidationError() - token = CallbackToken.objects.get(key=callback_token, is_active=True) + if email: + return 'email', email + elif mobile: + return 'mobile', mobile - if token: - # Check the token type for our uni-auth method. - # authenticates and checks the expiry of the callback token. - user = authenticate_by_token(token) - if user: - if not user.is_active: - msg = _('User account is disabled.') - raise serializers.ValidationError(msg) + return None - if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED \ - or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED: - # Mark this alias as verified - user = User.objects.get(pk=token.user.pk) - success = verify_user_alias(user, token) - if success is False: - msg = _('Error validating user alias.') +class CallbackTokenAuthSerializer(AbstractBaseCallbackTokenSerializer): + + def validate(self, attrs): + # Check Aliases + try: + alias_type, alias = self.validate_alias(attrs) + callback_token = attrs.get('token', None) + token = CallbackToken.objects.get(**{alias_type: alias, + 'key': callback_token, + 'type': CallbackToken.TOKEN_TYPE_AUTH, + 'is_active': True}) + + if token: + # Check the token type for our uni-auth method. + # authenticates and checks the expiry of the callback token. + user = authenticate_by_token(token) + if user: + if not user.is_active: + msg = _('User account is disabled.') raise serializers.ValidationError(msg) - attrs['user'] = user - return attrs + if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED \ + or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED: + # Mark this alias as verified + user = User.objects.get(pk=token.user.pk) + success = verify_user_alias(user, token) + + if success is False: + msg = _('Error validating user alias.') + raise serializers.ValidationError(msg) + attrs['user'] = user + return attrs + + else: + msg = _('Invalid Token') + raise serializers.ValidationError(msg) else: - msg = _('Invalid Token') + msg = _('Missing authentication token.') raise serializers.ValidationError(msg) - else: - msg = _('Missing authentication token.') + + except serializers.ValidationError(): + msg = _('Invalid alias parameters provided.') raise serializers.ValidationError(msg) @@ -216,15 +248,18 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer): def validate(self, attrs): try: + alias_type, alias = self.validate_alias(attrs) user_id = self.context.get("user_id") - callback_token = attrs.get('token', None) - - token = CallbackToken.objects.get(key=callback_token, is_active=True) user = User.objects.get(pk=user_id) + callback_token = attrs.get('token', None) - if token.user == user: - # Check that the token.user is the request.user + token = CallbackToken.objects.get(**{'user': user, + alias_type: alias, + 'key': callback_token, + 'type': CallbackToken.TOKEN_TYPE_VERIFY, + 'is_active': True}) + if token: # Mark this alias as verified success = verify_user_alias(user, token) if success is False: @@ -237,7 +272,7 @@ def validate(self, attrs): logger.debug("drfpasswordless: User token mismatch when verifying alias.") except CallbackToken.DoesNotExist: - msg = _('Missing authentication token.') + msg = _('We could not verify this alias.') logger.debug("drfpasswordless: Tried to validate alias with bad token.") pass except User.DoesNotExist: diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 8856cc9..7f36c07 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -88,7 +88,33 @@ def test_email_auth_failed(self): self.assertEqual(response.status_code, status.HTTP_200_OK) # Token sent to alias - challenge_data = {'token': '123456'} # Send an arbitrary token instead + challenge_data = {'email': self.email, 'token': '123456'} # Send an arbitrary token instead + + # Try to auth with the callback token + challenge_response = self.client.post(self.challenge_url, challenge_data) + self.assertEqual(challenge_response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_email_auth_missing_alias(self): + data = {'email': self.email} + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Token sent to alias + callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() + challenge_data = {'token': callback_token} # Missing Alias + + # Try to auth with the callback token + challenge_response = self.client.post(self.challenge_url, challenge_data) + self.assertEqual(challenge_response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_email_auth_bad_alias(self): + data = {'email': self.email} + response = self.client.post(self.url, data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Token sent to alias + callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() + challenge_data = {'email': 'abcde@example.com', 'token': callback_token} # Bad Alias # Try to auth with the callback token challenge_response = self.client.post(self.challenge_url, challenge_data) @@ -101,7 +127,7 @@ def test_email_auth_expired(self): # Token sent to alias callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - challenge_data = {'token': callback_token} + challenge_data = {'email': self.email, 'token': callback_token} data = {'email': self.email} response = self.client.post(self.url, data) @@ -130,7 +156,7 @@ def test_email_auth_success(self): # Token sent to alias callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - challenge_data = {'token': callback_token} + challenge_data = {'email': self.email, 'token': callback_token} # Try to auth with the callback token challenge_response = self.client.post(self.challenge_url, challenge_data) @@ -295,7 +321,7 @@ def test_mobile_auth_expired(self): # Token sent to alias first_callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - first_challenge_data = {'token': first_callback_token} + first_challenge_data = {'mobile': self.mobile, 'token': first_callback_token} data = {'mobile': self.mobile} second_response = self.client.post(self.url, data) @@ -303,7 +329,7 @@ def test_mobile_auth_expired(self): # Second token sent to alias second_callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - second_challenge_data = {'token': second_callback_token} + second_challenge_data = {'mobile': self.mobile, 'token': second_callback_token} # Try to auth with the old callback token challenge_response = self.client.post(self.challenge_url, first_challenge_data) @@ -324,7 +350,7 @@ def test_mobile_auth_success(self): # Token sent to alias callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - challenge_data = {'token': callback_token} + challenge_data = {'mobile': self.mobile, 'token': callback_token} # Try to auth with the callback token challenge_response = self.client.post(self.challenge_url, challenge_data) diff --git a/tests/test_verification.py b/tests/test_verification.py index 39f370b..c825d3f 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -37,7 +37,7 @@ def test_email_unverified_to_verified_and_back(self): # Verify a token exists for the user, sign in and check verified again callback = CallbackToken.objects.filter(user=user, is_active=True).first() - callback_data = {'token': callback} + callback_data = {'email': email, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) @@ -68,7 +68,7 @@ def test_email_unverified_to_verified_and_back(self): # Post callback token back. verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() - verify_callback_response = self.client.post(self.callback_verify, {'token': verify_token.key}) + verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) # Refresh User @@ -112,7 +112,7 @@ def test_mobile_unverified_to_verified_and_back(self): # Verify a token exists for the user, sign in and check verified again callback = CallbackToken.objects.filter(user=user, is_active=True).first() - callback_data = {'token': callback} + callback_data = {'mobile': mobile, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) @@ -143,7 +143,7 @@ def test_mobile_unverified_to_verified_and_back(self): # Post callback token back. verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() - verify_callback_response = self.client.post(self.callback_verify, {'token': verify_token.key}) + verify_callback_response = self.client.post(self.callback_verify, {'mobile': mobile2, 'token': verify_token.key}) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) # Refresh User From 32107c058e809b4215d695c39e2f0fb6f3acf405 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:23:40 -0800 Subject: [PATCH 15/40] Fix serializer logic --- drfpasswordless/serializers.py | 42 +++++++++++++++------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 1271fe8..737d40a 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -201,40 +201,35 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) callback_token = attrs.get('token', None) - token = CallbackToken.objects.get(**{alias_type: alias, - 'key': callback_token, + token = CallbackToken.objects.get(**{'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_AUTH, 'is_active': True}) - if token: + user = User.objects.get(**{alias_type: alias}) + + if token.user == user: # Check the token type for our uni-auth method. # authenticates and checks the expiry of the callback token. - user = authenticate_by_token(token) - if user: - if not user.is_active: - msg = _('User account is disabled.') - raise serializers.ValidationError(msg) + if not user.is_active: + msg = _('User account is disabled.') + raise serializers.ValidationError(msg) - if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED \ - or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED: - # Mark this alias as verified - user = User.objects.get(pk=token.user.pk) - success = verify_user_alias(user, token) + if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED \ + or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED: + # Mark this alias as verified + user = User.objects.get(pk=token.user.pk) + success = verify_user_alias(user, token) - if success is False: - msg = _('Error validating user alias.') - raise serializers.ValidationError(msg) + if success is False: + msg = _('Error validating user alias.') + raise serializers.ValidationError(msg) - attrs['user'] = user - return attrs + attrs['user'] = user + return attrs - else: - msg = _('Invalid Token') - raise serializers.ValidationError(msg) else: - msg = _('Missing authentication token.') + msg = _('Invalid Token') raise serializers.ValidationError(msg) - except serializers.ValidationError(): msg = _('Invalid alias parameters provided.') raise serializers.ValidationError(msg) @@ -254,7 +249,6 @@ def validate(self, attrs): callback_token = attrs.get('token', None) token = CallbackToken.objects.get(**{'user': user, - alias_type: alias, 'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) From 79ed09e33399dbcaf438033dca6eb5cbe4fc83c0 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:27:18 -0800 Subject: [PATCH 16/40] Tests --- drfpasswordless/serializers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 737d40a..f007c18 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -201,12 +201,12 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) callback_token = attrs.get('token', None) - token = CallbackToken.objects.get(**{'key': callback_token, + user = User.objects.get(**{alias_type: alias}) + token = CallbackToken.objects.get(**{'user': user, + 'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_AUTH, 'is_active': True}) - user = User.objects.get(**{alias_type: alias}) - if token.user == user: # Check the token type for our uni-auth method. # authenticates and checks the expiry of the callback token. @@ -230,6 +230,9 @@ def validate(self, attrs): else: msg = _('Invalid Token') raise serializers.ValidationError(msg) + except User.DoesNotExist: + msg = _('Invalid alias parameters provided.') + raise serializers.ValidationError(msg) except serializers.ValidationError(): msg = _('Invalid alias parameters provided.') raise serializers.ValidationError(msg) @@ -270,7 +273,7 @@ def validate(self, attrs): logger.debug("drfpasswordless: Tried to validate alias with bad token.") pass except User.DoesNotExist: - msg = _('Missing user.') + msg = _('We could not verify this alias.') logger.debug("drfpasswordless: Tried to validate alias with bad user.") pass except PermissionDenied: From e8df3a48fa99e9a2fa8ecfc567d3a34a5e7c5648 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:31:56 -0800 Subject: [PATCH 17/40] Tests --- drfpasswordless/serializers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index f007c18..b2fa0c7 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -4,6 +4,7 @@ from django.core.exceptions import PermissionDenied from django.core.validators import RegexValidator from rest_framework import serializers +from rest_framework.exceptions import ValidationError from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.utils import authenticate_by_token, verify_user_alias, validate_token_age @@ -233,7 +234,7 @@ def validate(self, attrs): except User.DoesNotExist: msg = _('Invalid alias parameters provided.') raise serializers.ValidationError(msg) - except serializers.ValidationError(): + except ValidationError: msg = _('Invalid alias parameters provided.') raise serializers.ValidationError(msg) @@ -248,7 +249,7 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) user_id = self.context.get("user_id") - user = User.objects.get(pk=user_id) + user = User.objects.get(**{'pk': user_id, alias_type: alias}) callback_token = attrs.get('token', None) token = CallbackToken.objects.get(**{'user': user, From d14970cb7e81c70e4ac8e5d69986d2614700404c Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:37:55 -0800 Subject: [PATCH 18/40] tests --- tests/test_authentication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 7f36c07..ca3d44a 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -135,7 +135,7 @@ def test_email_auth_expired(self): # Second token sent to alias second_callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - second_challenge_data = {'token': second_callback_token} + second_challenge_data = {'email': self.email, 'token': second_callback_token} # Try to auth with the old callback token challenge_response = self.client.post(self.challenge_url, challenge_data) @@ -268,7 +268,7 @@ def test_token_creation_gets_overridden(self): # Token sent to alias callback_token = CallbackToken.objects.filter(user=self.user, is_active=True).first() - challenge_data = {'token': callback_token} + challenge_data = {'email': self.email, 'token': callback_token} # Try to auth with the callback token challenge_response = self.client.post(self.challenge_url, challenge_data) @@ -308,7 +308,7 @@ def test_mobile_auth_failed(self): self.assertEqual(response.status_code, status.HTTP_200_OK) # Token sent to alias - challenge_data = {'token': '123456'} # Send an arbitrary token instead + challenge_data = {'mobile': self.mobile, 'token': '123456'} # Send an arbitrary token instead # Try to auth with the callback token challenge_response = self.client.post(self.challenge_url, challenge_data) From a5a1bc779036a19e2cfea9cc8e061fa08c5ced54 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:42:46 -0800 Subject: [PATCH 19/40] Tests --- tests/test_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_settings.py b/tests/test_settings.py index f016d55..a6799d3 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -108,7 +108,7 @@ def test_email_unverified_to_verified_and_back(self): # Verify a token exists for the user, sign in and check verified again callback = CallbackToken.objects.filter(user=user, is_active=True).first() - callback_data = {'token': callback} + callback_data = {'email': email, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) @@ -158,7 +158,7 @@ def test_mobile_unverified_to_verified_and_back(self): # Verify a token exists for the user, sign in and check verified again callback = CallbackToken.objects.filter(user=user, is_active=True).first() - callback_data = {'token': callback} + callback_data = {'mobile': mobile, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) From 572eb34edede58295b51ce8f53d41d1daa6bafef Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:47:40 -0800 Subject: [PATCH 20/40] Tests --- tests/test_verification.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_verification.py b/tests/test_verification.py index c825d3f..6a73527 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -68,6 +68,7 @@ def test_email_unverified_to_verified_and_back(self): # Post callback token back. verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() + self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) From a031fc4945dd4ef91dfbe1cfebdd5a61e641718d Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:52:25 -0800 Subject: [PATCH 21/40] Tests --- tests/test_verification.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_verification.py b/tests/test_verification.py index 6a73527..f914c6e 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -144,6 +144,7 @@ def test_mobile_unverified_to_verified_and_back(self): # Post callback token back. verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() + self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'mobile': mobile2, 'token': verify_token.key}) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) From d25d8a3a9e4af59cc0ba1e0c85c4733bfa25de7c Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 01:55:07 -0800 Subject: [PATCH 22/40] Tests --- tests/test_verification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_verification.py b/tests/test_verification.py index f914c6e..d74a6a8 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -70,7 +70,7 @@ def test_email_unverified_to_verified_and_back(self): verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) - self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) + self.assertEqual(verify_callback_response, status.HTTP_200_OK) # Refresh User user = User.objects.get(**{self.email_field_name: email2}) From e44dcef1e5605d30c289d044f122d922c593ce31 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:01:36 -0800 Subject: [PATCH 23/40] Test --- drfpasswordless/serializers.py | 2 +- tests/test_verification.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index b2fa0c7..19bffb5 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -270,7 +270,7 @@ def validate(self, attrs): logger.debug("drfpasswordless: User token mismatch when verifying alias.") except CallbackToken.DoesNotExist: - msg = _('We could not verify this alias.') + msg = _('We could not verify this token.') logger.debug("drfpasswordless: Tried to validate alias with bad token.") pass except User.DoesNotExist: diff --git a/tests/test_verification.py b/tests/test_verification.py index d74a6a8..f914c6e 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -70,7 +70,7 @@ def test_email_unverified_to_verified_and_back(self): verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) - self.assertEqual(verify_callback_response, status.HTTP_200_OK) + self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) # Refresh User user = User.objects.get(**{self.email_field_name: email2}) From ec1d781ab433bebfd9eea046ef2845ca6c310a17 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:02:21 -0800 Subject: [PATCH 24/40] Tests --- drfpasswordless/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 19bffb5..01ebc90 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -249,7 +249,7 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) user_id = self.context.get("user_id") - user = User.objects.get(**{'pk': user_id, alias_type: alias}) + user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) token = CallbackToken.objects.get(**{'user': user, @@ -270,7 +270,7 @@ def validate(self, attrs): logger.debug("drfpasswordless: User token mismatch when verifying alias.") except CallbackToken.DoesNotExist: - msg = _('We could not verify this token.') + msg = _('We could not verify this alias.') logger.debug("drfpasswordless: Tried to validate alias with bad token.") pass except User.DoesNotExist: From b98f1ac645a8a1ab5e7afc37c7777a250dc4e984 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:06:07 -0800 Subject: [PATCH 25/40] Tests --- drfpasswordless/serializers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 01ebc90..8133cf6 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -8,6 +8,7 @@ from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.utils import authenticate_by_token, verify_user_alias, validate_token_age +import sys logger = logging.getLogger(__name__) User = get_user_model() @@ -249,8 +250,12 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) user_id = self.context.get("user_id") + + print(user_id, file=sys.stderr) user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) + print(alias_type, file=sys.stderr) + print(alias, file=sys.stderr) token = CallbackToken.objects.get(**{'user': user, 'key': callback_token, From ed91f8e1908bba113578bf17a71ebb614624733d Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:08:49 -0800 Subject: [PATCH 26/40] Tests --- drfpasswordless/serializers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 8133cf6..48dd05a 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -262,15 +262,20 @@ def validate(self, attrs): 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) + print("test", file=sys.stderr) + if token: + print("test2", file=sys.stderr) # Mark this alias as verified success = verify_user_alias(user, token) if success is False: + print("test3", file=sys.stderr) logger.debug("drfpasswordless: Error verifying alias.") attrs['user'] = user return attrs else: + print("test4", file=sys.stderr) msg = _('This token is invalid. Try again later.') logger.debug("drfpasswordless: User token mismatch when verifying alias.") From e511e42f2c444aed600f60d1a8a75c3aa7891466 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:11:21 -0800 Subject: [PATCH 27/40] Tests --- drfpasswordless/serializers.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 48dd05a..45535c4 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -250,32 +250,22 @@ def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) user_id = self.context.get("user_id") - - print(user_id, file=sys.stderr) user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) - print(alias_type, file=sys.stderr) - print(alias, file=sys.stderr) - token = CallbackToken.objects.get(**{'user': user, - 'key': callback_token, + token = CallbackToken.objects.get(**{'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) - print("test", file=sys.stderr) - if token: - print("test2", file=sys.stderr) # Mark this alias as verified success = verify_user_alias(user, token) if success is False: - print("test3", file=sys.stderr) logger.debug("drfpasswordless: Error verifying alias.") attrs['user'] = user return attrs else: - print("test4", file=sys.stderr) msg = _('This token is invalid. Try again later.') logger.debug("drfpasswordless: User token mismatch when verifying alias.") From 903d824988368221af9cac2d99470d48660f58d0 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:18:19 -0800 Subject: [PATCH 28/40] Tests --- drfpasswordless/serializers.py | 10 +++++----- drfpasswordless/views.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 45535c4..104c6da 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -8,7 +8,6 @@ from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.utils import authenticate_by_token, verify_user_alias, validate_token_age -import sys logger = logging.getLogger(__name__) User = get_user_model() @@ -249,15 +248,16 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer): def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) - user_id = self.context.get("user_id") - user = User.objects.get(**{'id': user_id, alias_type: alias}) + request = self.context['request'].user + user = User.objects.get(**{'id': request.user.id, alias_type: alias}) callback_token = attrs.get('token', None) - token = CallbackToken.objects.get(**{'key': callback_token, + token = CallbackToken.objects.get(**{'user': user, + 'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) - if token: + if token.user == user: # Mark this alias as verified success = verify_user_alias(user, token) if success is False: diff --git a/drfpasswordless/views.py b/drfpasswordless/views.py index 500cfbb..0a66744 100644 --- a/drfpasswordless/views.py +++ b/drfpasswordless/views.py @@ -168,7 +168,7 @@ class VerifyAliasFromCallbackToken(APIView): serializer_class = CallbackTokenVerificationSerializer def post(self, request, *args, **kwargs): - serializer = self.serializer_class(data=request.data, context={'user_id': self.request.user.id}) + serializer = self.serializer_class(data=request.data) if serializer.is_valid(raise_exception=True): return Response({'detail': 'Alias verified.'}, status=status.HTTP_200_OK) else: From 1414840f860c89ea669b96d5b1b1c09901e4957e Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:20:51 -0800 Subject: [PATCH 29/40] Tests --- drfpasswordless/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 104c6da..ba957a8 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -248,7 +248,7 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer): def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) - request = self.context['request'].user + request = self.context['request'] user = User.objects.get(**{'id': request.user.id, alias_type: alias}) callback_token = attrs.get('token', None) From e71231cc32dca57ab135eaeb1321722f82ea7a34 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:27:46 -0800 Subject: [PATCH 30/40] Tests --- drfpasswordless/serializers.py | 16 ++++++++++++++-- drfpasswordless/views.py | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index ba957a8..521b7e7 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -8,6 +8,7 @@ from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.utils import authenticate_by_token, verify_user_alias, validate_token_age +import sys logger = logging.getLogger(__name__) User = get_user_model() @@ -248,10 +249,21 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer): def validate(self, attrs): try: alias_type, alias = self.validate_alias(attrs) - request = self.context['request'] - user = User.objects.get(**{'id': request.user.id, alias_type: alias}) + user_id = self.context.get("user_id") + user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) + print(user_id, file=sys.stderr) + print(user.email, file=sys.stderr) + print(user.id, file=sys.stderr) + print(callback_token, file=sys.stderr) + + tokenx = CallbackToken.objects.filter(key=callback_token) + print(tokenx.user.id, file=sys.stderr) + print(tokenx.key, file=sys.stderr) + print(tokenx.type, file=sys.stderr) + print(tokenx.is_active, file=sys.stderr) + token = CallbackToken.objects.get(**{'user': user, 'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, diff --git a/drfpasswordless/views.py b/drfpasswordless/views.py index 0a66744..500cfbb 100644 --- a/drfpasswordless/views.py +++ b/drfpasswordless/views.py @@ -168,7 +168,7 @@ class VerifyAliasFromCallbackToken(APIView): serializer_class = CallbackTokenVerificationSerializer def post(self, request, *args, **kwargs): - serializer = self.serializer_class(data=request.data) + serializer = self.serializer_class(data=request.data, context={'user_id': self.request.user.id}) if serializer.is_valid(raise_exception=True): return Response({'detail': 'Alias verified.'}, status=status.HTTP_200_OK) else: From c662b3cadbcbc6356bd06e299bb198df6235f399 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:29:31 -0800 Subject: [PATCH 31/40] tests --- drfpasswordless/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 521b7e7..7695742 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -258,7 +258,7 @@ def validate(self, attrs): print(user.id, file=sys.stderr) print(callback_token, file=sys.stderr) - tokenx = CallbackToken.objects.filter(key=callback_token) + tokenx = CallbackToken.objects.filter(key=callback_token).first() print(tokenx.user.id, file=sys.stderr) print(tokenx.key, file=sys.stderr) print(tokenx.type, file=sys.stderr) From f441200f4d53ca7b2f8967ef28b6bfcef6363dba Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:31:48 -0800 Subject: [PATCH 32/40] Tests --- drfpasswordless/serializers.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 7695742..d3a2354 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -8,7 +8,6 @@ from drfpasswordless.models import CallbackToken from drfpasswordless.settings import api_settings from drfpasswordless.utils import authenticate_by_token, verify_user_alias, validate_token_age -import sys logger = logging.getLogger(__name__) User = get_user_model() @@ -253,18 +252,7 @@ def validate(self, attrs): user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) - print(user_id, file=sys.stderr) - print(user.email, file=sys.stderr) - print(user.id, file=sys.stderr) - print(callback_token, file=sys.stderr) - - tokenx = CallbackToken.objects.filter(key=callback_token).first() - print(tokenx.user.id, file=sys.stderr) - print(tokenx.key, file=sys.stderr) - print(tokenx.type, file=sys.stderr) - print(tokenx.is_active, file=sys.stderr) - - token = CallbackToken.objects.get(**{'user': user, + token = CallbackToken.objects.get(**{'user_id': user.id, 'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) From bc1dbbe0c4e8a0c6a43a25e05a70eb2955ee3b2b Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:33:26 -0800 Subject: [PATCH 33/40] Tests --- drfpasswordless/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index d3a2354..dfebe06 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -252,8 +252,7 @@ def validate(self, attrs): user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) - token = CallbackToken.objects.get(**{'user_id': user.id, - 'key': callback_token, + token = CallbackToken.objects.get(**{'key': callback_token, 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) From 9de8327db67133519bec3405066e6ce95572ada0 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:39:26 -0800 Subject: [PATCH 34/40] Tests --- drfpasswordless/serializers.py | 1 - drfpasswordless/utils.py | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index dfebe06..01d72ae 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -253,7 +253,6 @@ def validate(self, attrs): callback_token = attrs.get('token', None) token = CallbackToken.objects.get(**{'key': callback_token, - 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) if token.user == user: diff --git a/drfpasswordless/utils.py b/drfpasswordless/utils.py index 2bdc106..4c32580 100644 --- a/drfpasswordless/utils.py +++ b/drfpasswordless/utils.py @@ -85,11 +85,9 @@ def verify_user_alias(user, token): Marks a user's contact point as verified depending on accepted token type. """ if token.to_alias_type == 'EMAIL': - if token.to_alias == getattr(user, api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME): - setattr(user, api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME, True) + setattr(user, api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME, True) elif token.to_alias_type == 'MOBILE': - if token.to_alias == getattr(user, api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME): - setattr(user, api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME, True) + setattr(user, api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME, True) else: return False user.save() From a0d41bcb9872462022add20b0ecfa0fd9a0a140e Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:39:57 -0800 Subject: [PATCH 35/40] Tests --- drfpasswordless/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drfpasswordless/serializers.py b/drfpasswordless/serializers.py index 01d72ae..f59623c 100644 --- a/drfpasswordless/serializers.py +++ b/drfpasswordless/serializers.py @@ -252,7 +252,9 @@ def validate(self, attrs): user = User.objects.get(**{'id': user_id, alias_type: alias}) callback_token = attrs.get('token', None) - token = CallbackToken.objects.get(**{'key': callback_token, + token = CallbackToken.objects.get(**{'user': user, + 'key': callback_token, + 'type': CallbackToken.TOKEN_TYPE_VERIFY, 'is_active': True}) if token.user == user: From 75fa745a0f6e6bcfd6a7160c7e7a2e0448b2fe80 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:43:10 -0800 Subject: [PATCH 36/40] Tests --- tests/test_verification.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_verification.py b/tests/test_verification.py index f914c6e..5e40c54 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -5,6 +5,7 @@ from django.urls import reverse from drfpasswordless.settings import api_settings, DEFAULTS from drfpasswordless.utils import CallbackToken +import sys User = get_user_model() @@ -70,6 +71,10 @@ def test_email_unverified_to_verified_and_back(self): verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) + print(verify_token.id, file=sys.stderr) + print(verify_token.user.id, file=sys.stderr) + print(verify_token.is_active, file=sys.stderr) + self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) # Refresh User From 52b3116e9f56fe1d8a9fa853f7536c1775159537 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:46:44 -0800 Subject: [PATCH 37/40] Tests --- tests/test_verification.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_verification.py b/tests/test_verification.py index 5e40c54..68b517c 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -73,7 +73,12 @@ def test_email_unverified_to_verified_and_back(self): verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) print(verify_token.id, file=sys.stderr) print(verify_token.user.id, file=sys.stderr) + print(verify_token.user.email, file=sys.stderr) + print(verify_token.type, file=sys.stderr) print(verify_token.is_active, file=sys.stderr) + print(verify_token.key, file=sys.stderr) + print(user.id, file=sys.stderr) + print(user.email, file=sys.stderr) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) From d29dc799ba6a93d61b262e1f07b312912376ba7f Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:51:31 -0800 Subject: [PATCH 38/40] Tests --- tests/test_verification.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/test_verification.py b/tests/test_verification.py index 68b517c..cb31493 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -5,7 +5,6 @@ from django.urls import reverse from drfpasswordless.settings import api_settings, DEFAULTS from drfpasswordless.utils import CallbackToken -import sys User = get_user_model() @@ -37,7 +36,7 @@ def test_email_unverified_to_verified_and_back(self): self.assertEqual(getattr(user, self.email_verified_field_name), False) # Verify a token exists for the user, sign in and check verified again - callback = CallbackToken.objects.filter(user=user, is_active=True).first() + callback = CallbackToken.objects.filter(user=user, type=CallbackToken.TOKEN_TYPE_AUTH, is_active=True).first() callback_data = {'email': email, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) @@ -68,18 +67,9 @@ def test_email_unverified_to_verified_and_back(self): self.assertEqual(getattr(user, self.email_verified_field_name), False) # Post callback token back. - verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() + verify_token = CallbackToken.objects.filter(user=user, type=CallbackToken.TOKEN_TYPE_VERIFY, is_active=True).first() self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'email': email2, 'token': verify_token.key}) - print(verify_token.id, file=sys.stderr) - print(verify_token.user.id, file=sys.stderr) - print(verify_token.user.email, file=sys.stderr) - print(verify_token.type, file=sys.stderr) - print(verify_token.is_active, file=sys.stderr) - print(verify_token.key, file=sys.stderr) - print(user.id, file=sys.stderr) - print(user.email, file=sys.stderr) - self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK) # Refresh User From 6c7b53b4733e1197487b0e73e038d32eaf1c435c Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:52:27 -0800 Subject: [PATCH 39/40] Tests --- drfpasswordless/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drfpasswordless/utils.py b/drfpasswordless/utils.py index 4c32580..2bdc106 100644 --- a/drfpasswordless/utils.py +++ b/drfpasswordless/utils.py @@ -85,9 +85,11 @@ def verify_user_alias(user, token): Marks a user's contact point as verified depending on accepted token type. """ if token.to_alias_type == 'EMAIL': - setattr(user, api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME, True) + if token.to_alias == getattr(user, api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME): + setattr(user, api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME, True) elif token.to_alias_type == 'MOBILE': - setattr(user, api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME, True) + if token.to_alias == getattr(user, api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME): + setattr(user, api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME, True) else: return False user.save() From 7339bcd88b7a3998b3589e7fe5bc0cda5a793238 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 24 Jan 2020 02:53:32 -0800 Subject: [PATCH 40/40] Tests --- tests/test_verification.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_verification.py b/tests/test_verification.py index cb31493..43a846d 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -112,7 +112,7 @@ def test_mobile_unverified_to_verified_and_back(self): self.assertEqual(getattr(user, self.mobile_verified_field_name), False) # Verify a token exists for the user, sign in and check verified again - callback = CallbackToken.objects.filter(user=user, is_active=True).first() + callback = CallbackToken.objects.filter(user=user, type=CallbackToken.TOKEN_TYPE_AUTH, is_active=True).first() callback_data = {'mobile': mobile, 'token': callback} callback_response = self.client.post(self.callback_url, callback_data) self.assertEqual(callback_response.status_code, status.HTTP_200_OK) @@ -143,7 +143,7 @@ def test_mobile_unverified_to_verified_and_back(self): self.assertEqual(getattr(user, self.mobile_verified_field_name), False) # Post callback token back. - verify_token = CallbackToken.objects.filter(user=user, is_active=True).first() + verify_token = CallbackToken.objects.filter(user=user, type=CallbackToken.TOKEN_TYPE_VERIFY, is_active=True).first() self.assertNotEqual(verify_token, None) verify_callback_response = self.client.post(self.callback_verify, {'mobile': mobile2, 'token': verify_token.key}) self.assertEqual(verify_callback_response.status_code, status.HTTP_200_OK)