From fe546f43f2c7c9a1f7fafbaae9f59147d4506f79 Mon Sep 17 00:00:00 2001 From: Andrew Buss Date: Sun, 31 May 2015 01:55:30 -0700 Subject: [PATCH] move verify_email outside user update --- flashcards/models.py | 2 +- flashcards/serializers.py | 11 +++-------- flashcards/tests/test_api.py | 8 ++++---- flashcards/views.py | 33 +++++++++++++++++++++++---------- flashy/urls.py | 3 ++- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/flashcards/models.py b/flashcards/models.py index fd8dd0b..96c5276 100644 --- a/flashcards/models.py +++ b/flashcards/models.py @@ -143,7 +143,7 @@ class User(AbstractUser, SimpleEmailConfirmationUserMixin): self.save() def by_retention(self, sections, material_date_begin, material_date_end): - section_pks = map(lambda i: i['pk'], sections.values('pk')) + section_pks = sections.values_list('pk') user_flashcard_filter = UserFlashcard.objects.filter( user=self, flashcard__section__pk__in=section_pks, flashcard__material_date__gte=material_date_begin, diff --git a/flashcards/serializers.py b/flashcards/serializers.py index e31eacd..ef0830f 100644 --- a/flashcards/serializers.py +++ b/flashcards/serializers.py @@ -46,11 +46,12 @@ class PasswordResetSerializer(Serializer): except User.DoesNotExist: raise serializers.ValidationError('Could not verify reset token') +class EmailVerificationSerializer(Serializer): + confirmation_key = CharField() class UserUpdateSerializer(Serializer): old_password = CharField(required=False) - new_password = CharField(required=False, allow_blank=False) - confirmation_key = CharField(required=False) + new_password = CharField(required=False, allow_blank=False)\ # reset_token = CharField(required=False) def validate(self, data): @@ -58,12 +59,6 @@ class UserUpdateSerializer(Serializer): raise serializers.ValidationError('old_password is required to set a new_password') return data - -# class Password(Serializer): -# email = EmailField(required=True) -# password = CharField(required=True) - - class LecturePeriodSerializer(ModelSerializer): class Meta: model = LecturePeriod diff --git a/flashcards/tests/test_api.py b/flashcards/tests/test_api.py index c92bb2c..c02f40d 100644 --- a/flashcards/tests/test_api.py +++ b/flashcards/tests/test_api.py @@ -120,13 +120,13 @@ class RegistrationTest(APITestCase): # try activating with an invalid key - url = '/api/me/' - response = self.client.patch(url, {'confirmation_key': 'NOT A KEY'}) + url = '/api/verify_email/' + response = self.client.post(url, {'confirmation_key': 'NOT A KEY'}) self.assertContains(response, 'confirmation_key is invalid', status_code=400) # try activating with the valid key - response = self.client.patch(url, {'confirmation_key': user.confirmation_key}) - self.assertTrue(response.data['is_confirmed']) + response = self.client.post(url, {'confirmation_key': user.confirmation_key}) + self.assertContains(response, '', status_code=204) class ProfileViewTest(APITestCase): diff --git a/flashcards/views.py b/flashcards/views.py index dbf5451..daab3ed 100644 --- a/flashcards/views.py +++ b/flashcards/views.py @@ -10,7 +10,7 @@ from flashcards.notifications import notify_new_card from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \ PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \ FlashcardUpdateSerializer, QuizRequestSerializer, QuizResponseSerializer, \ - QuizAnswerRequestSerializer, DeepSectionSerializer + QuizAnswerRequestSerializer, DeepSectionSerializer, EmailVerificationSerializer from rest_framework.decorators import detail_route, permission_classes, api_view, list_route from rest_framework.generics import ListAPIView, GenericAPIView from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin @@ -23,6 +23,7 @@ from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_20 from rest_framework.response import Response from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated, ValidationError, PermissionDenied from simple_email_confirmation import EmailAddress +from simple_email_confirmation.models import EmailAddressManager def log_event(request, event=''): @@ -144,7 +145,7 @@ class UserDetail(GenericAPIView): def patch(self, request, format=None): """ - Updates the user's password, or verifies their email address + Updates the user's password --- request_serializer: UserUpdateSerializer response_serializer: UserSerializer @@ -160,13 +161,6 @@ class UserDetail(GenericAPIView): request.user.save() log_event(request, 'change password') - if 'confirmation_key' in data: - try: - request.user.confirm_email(data['confirmation_key']) - log_event(request, 'confirm email') - except EmailAddress.DoesNotExist: - raise ValidationError('confirmation_key is invalid') - return Response(UserSerializer(request.user).data) def get(self, request, format=None): @@ -189,13 +183,32 @@ class UserDetail(GenericAPIView): return Response(status=HTTP_204_NO_CONTENT) - @api_view(['POST']) @permission_classes([IsAuthenticated]) def resend_confirmation_email(request): + "Resends a confirmation email to a user" request.user.send_confirmation_email() return Response(status=HTTP_204_NO_CONTENT) + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def verify_email(request): + """ + Accepts a user's email confirmation_key to verify their email address + --- + request_serializer: EmailVerificationSerializer + """ + try: + data = EmailVerificationSerializer(data=request.data) + data.is_valid(raise_exception=True) + email = EmailAddress.objects.confirm(data.validated_data['confirmation_key']) + log_event(request, 'confirm email' + str(email)) + return Response(status=HTTP_204_NO_CONTENT) + except EmailAddress.DoesNotExist: + raise ValidationError('confirmation_key is invalid') + + @api_view(['POST']) def register(request, format=None): """ diff --git a/flashy/urls.py b/flashy/urls.py index 68262f6..cb87c09 100644 --- a/flashy/urls.py +++ b/flashy/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import include, url from django.contrib import admin from flashcards.views import SectionViewSet, UserDetail, FlashcardViewSet, UserSectionListView, request_password_reset, \ - reset_password, logout, login, register, UserFlashcardQuizViewSet, resend_confirmation_email + reset_password, logout, login, register, UserFlashcardQuizViewSet, resend_confirmation_email, verify_email from flashy.frontend_serve import serve_with_default from flashy.settings import DEBUG, IN_PRODUCTION from rest_framework.routers import DefaultRouter @@ -20,6 +20,7 @@ urlpatterns = [ url(r'^api/logout/$', logout), url(r'^api/me/sections/', UserSectionListView.as_view()), url(r'^api/resend_confirmation_email/', resend_confirmation_email), + url(r'^api/verify_email/', verify_email), url(r'^api/request_password_reset/', request_password_reset), url(r'^api/reset_password/', reset_password), url(r'^api/', include(router.urls)), -- 1.9.1