Commit 25cc754dea73a51d30ff6415dde4f47500d8c298
1 parent
15af7095ef
Exists in
master
api/reset_password post and patch added, not done
Showing 2 changed files with 55 additions and 1 deletions Inline Diff
flashcards/api.py
View file @
25cc754
from django.core.mail import send_mail | 1 | 1 | from django.core.mail import send_mail | |
from django.contrib.auth import authenticate, login | 2 | 2 | from django.contrib.auth import authenticate, login | |
3 | from django.contrib.auth.tokens import default_token_generator | |||
from rest_framework.views import APIView | 3 | 4 | from rest_framework.views import APIView | |
from rest_framework.response import Response | 4 | 5 | from rest_framework.response import Response | |
from rest_framework import status | 5 | 6 | from rest_framework import status | |
from rest_framework.exceptions import ValidationError | 6 | 7 | from rest_framework.exceptions import ValidationError, NotFound | |
from flashcards.serializers import * | 7 | 8 | from flashcards.serializers import * | |
8 | 9 | |||
9 | 10 | |||
class UserDetail(APIView): | 10 | 11 | class UserDetail(APIView): | |
def patch(self, request, format=None): | 11 | 12 | def patch(self, request, format=None): | |
""" | 12 | 13 | """ | |
Updates a user's password after they enter a valid old password. | 13 | 14 | Updates a user's password after they enter a valid old password. | |
TODO: email verification | 14 | 15 | TODO: email verification | |
""" | 15 | 16 | """ | |
16 | 17 | |||
if 'old_password' not in request.data: | 17 | 18 | if 'old_password' not in request.data: | |
raise ValidationError('Old password is required') | 18 | 19 | raise ValidationError('Old password is required') | |
if 'new_password' not in request.data: | 19 | 20 | if 'new_password' not in request.data: | |
raise ValidationError('New password is required') | 20 | 21 | raise ValidationError('New password is required') | |
if not request.data['new_password']: | 21 | 22 | if not request.data['new_password']: | |
raise ValidationError('Password cannot be blank') | 22 | 23 | raise ValidationError('Password cannot be blank') | |
23 | 24 | |||
currentuser = request.user | 24 | 25 | currentuser = request.user | |
25 | 26 | |||
if not currentuser.check_password(request.data['old_password']): | 26 | 27 | if not currentuser.check_password(request.data['old_password']): | |
raise ValidationError('Invalid old password') | 27 | 28 | raise ValidationError('Invalid old password') | |
28 | 29 | |||
currentuser.set_password(request.data['new_password']) | 29 | 30 | currentuser.set_password(request.data['new_password']) | |
currentuser.save() | 30 | 31 | currentuser.save() | |
31 | 32 | |||
return Response(status=status.HTTP_204_NO_CONTENT) | 32 | 33 | return Response(status=status.HTTP_204_NO_CONTENT) | |
33 | 34 | |||
def get(self, request, format=None): | 34 | 35 | def get(self, request, format=None): | |
serializer = UserSerializer(request.user) | 35 | 36 | serializer = UserSerializer(request.user) | |
return Response(serializer.data) | 36 | 37 | return Response(serializer.data) | |
37 | 38 | |||
def post(self, request, format=None): | 38 | 39 | def post(self, request, format=None): | |
if 'email' not in request.data: | 39 | 40 | if 'email' not in request.data: | |
raise ValidationError('Email is required') | 40 | 41 | raise ValidationError('Email is required') | |
if 'password' not in request.data: | 41 | 42 | if 'password' not in request.data: | |
raise ValidationError('Password is required') | 42 | 43 | raise ValidationError('Password is required') | |
43 | 44 | |||
email = request.data['email'] | 44 | 45 | email = request.data['email'] | |
user = User.objects.create_user(email) | 45 | 46 | user = User.objects.create_user(email) | |
46 | 47 | |||
body = ''' | 47 | 48 | body = ''' | |
Visit the following link to confirm your email address: | 48 | 49 | Visit the following link to confirm your email address: | |
http://flashy.cards/app/verify_email/%s | 49 | 50 | http://flashy.cards/app/verify_email/%s | |
50 | 51 | |||
If you did not register for Flashy, no action is required. | 51 | 52 | If you did not register for Flashy, no action is required. | |
''' | 52 | 53 | ''' | |
53 | 54 | |||
send_mail("Please verify your Flashy account", | 54 | 55 | send_mail("Please verify your Flashy account", | |
body % user.confirmation_key, | 55 | 56 | body % user.confirmation_key, | |
"noreply@flashy.cards", | 56 | 57 | "noreply@flashy.cards", | |
[user.email]) | 57 | 58 | [user.email]) | |
58 | 59 | |||
return Response(UserSerializer(User).data) | 59 | 60 | return Response(UserSerializer(User).data) | |
60 | 61 | |||
def delete(self, request, format=None): | 61 | 62 | def delete(self, request, format=None): | |
request.user.delete() | 62 | 63 | request.user.delete() | |
return Response(status=status.HTTP_204_NO_CONTENT) | 63 | 64 | return Response(status=status.HTTP_204_NO_CONTENT) | |
64 | 65 | |||
class UserLogin(APIView): | 65 | 66 | class UserLogin(APIView): | |
""" | 66 | 67 | """ | |
Authenticates user and returns user data if valid. Handles invalid | 67 | 68 | Authenticates user and returns user data if valid. Handles invalid | |
users. | 68 | 69 | users. | |
""" | 69 | 70 | """ | |
def post(self, request, format=None): | 70 | 71 | def post(self, request, format=None): | |
72 | """ | |||
73 | Returns user data if valid. | |||
74 | """ | |||
if 'email' not in request.data: | 71 | 75 | if 'email' not in request.data: | |
raise ValidationError('Email is required') | 72 | 76 | raise ValidationError('Email is required') | |
if 'password' not in request.data: | 73 | 77 | if 'password' not in request.data: | |
raise ValidationError('Password is required') | 74 | 78 | raise ValidationError('Password is required') | |
75 | 79 | |||
email = request.data['email'] | 76 | 80 | email = request.data['email'] | |
password = request.data['password'] | 77 | 81 | password = request.data['password'] | |
user = authenticate(username=email, password=password) | 78 | 82 | user = authenticate(username=email, password=password) | |
79 | 83 | |||
if user is not None: | 80 | 84 | if user is not None: | |
if user.is_active: | 81 | 85 | if user.is_active: | |
login(request, user) | 82 | 86 | login(request, user) | |
return Response(UserSerializer(User).data) | 83 | 87 | return Response(UserSerializer(User).data) | |
else: | 84 | 88 | else: | |
raise ValidationError('Account is disabled') | 85 | 89 | raise ValidationError('Account is disabled') | |
else: | 86 | 90 | else: | |
raise ValidationError('Invalid email or password') | 87 | 91 | raise ValidationError('Invalid email or password') | |
92 | ||||
93 | class PasswordReset(APIView): | |||
94 | """ | |||
95 | Allows user to reset their password. | |||
96 | """ | |||
97 | def post(self, request, format=None): | |||
98 | """ | |||
99 | Send a password reset token/link to the provided email. | |||
100 | """ | |||
101 | if 'email' not in request.data: | |||
102 | raise ValidationError('Email is required') | |||
103 | ||||
104 | email = request.data['email'] | |||
105 | ||||
106 | # Find the user since they are not logged in. | |||
107 | try: | |||
108 | user = User.objects.get(email=email) | |||
109 | except User.DoesNotExist: | |||
110 | raise NotFound('Email does not exist') | |||
111 | ||||
112 | token = default_token_generator.make_token(user) | |||
113 | ||||
114 | body = ''' | |||
115 | Visit the following link to reset your password: | |||
116 | http://flashy.cards/app/reset_password/%d/%s | |||
117 | ||||
118 | If you did not request a password reset, no action is required. | |||
119 | ''' | |||
120 | ||||
121 | send_mail("Please verify your Flashy account", | |||
122 | body % (user.pk, token), | |||
123 | "noreply@flashy.cards", |
flashy/urls.py
View file @
25cc754
from django.conf.urls import include, url | 1 | 1 | from django.conf.urls import include, url | |
from django.contrib import admin | 2 | 2 | from django.contrib import admin | |
from flashcards.views import SectionViewSet, LecturePeriodViewSet | 3 | 3 | from flashcards.views import SectionViewSet, LecturePeriodViewSet | |
from rest_framework.routers import DefaultRouter | 4 | 4 | from rest_framework.routers import DefaultRouter | |
from flashcards.api import * | 5 | 5 | from flashcards.api import * | |
6 | 6 | |||
router = DefaultRouter() | 7 | 7 | router = DefaultRouter() | |
router.register(r'sections', SectionViewSet) | 8 | 8 | router.register(r'sections', SectionViewSet) | |
router.register(r'lectureperiods', LecturePeriodViewSet) | 9 | 9 | router.register(r'lectureperiods', LecturePeriodViewSet) | |
10 | 10 | |||
urlpatterns = [ | 11 | 11 | urlpatterns = [ | |
url(r'^api/users/me$', UserDetail.as_view()), | 12 | 12 | url(r'^api/users/me$', UserDetail.as_view()), | |
url(r'^api/login$', UserLogin.as_view()), | 13 | 13 | url(r'^api/login$', UserLogin.as_view()), | |
14 | url(r'^api/reset_password$', PasswordReset.as_view()), | |||
url(r'^api/', include(router.urls)), | 14 | 15 | url(r'^api/', include(router.urls)), | |
url(r'^admin/doc/', include('django.contrib.admindocs.urls')), | 15 | 16 | url(r'^admin/doc/', include('django.contrib.admindocs.urls')), | |
url(r'^admin/', include(admin.site.urls)), | 16 | 17 | url(r'^admin/', include(admin.site.urls)), | |
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) | 17 | 18 | url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) |