forked from aaronn/django-rest-framework-passwordless
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathviews.py
184 lines (145 loc) · 7.35 KB
/
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import logging
from rest_framework import parsers, renderers, status
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.views import APIView
from .settings import api_settings
from .serializers import EmailAuthSerializer, MobileAuthSerializer, CallbackTokenAuthSerializer, CallbackTokenVerificationSerializer
from .utils import send_sms_with_callback_token, send_email_with_callback_token, create_callback_token_for_user
log = logging.getLogger(__name__)
class AbstractBaseObtainCallbackToken(APIView):
"""
This returns a 6-digit callback token we can trade for a user's Auth Token.
"""
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
success_response = "A login token has been sent to you."
failure_response = "Unable to send you a login code. Try again later."
message_payload = {}
@property
def serializer_class(self):
# Our serializer depending on type
raise NotImplementedError
@property
def alias_type(self):
# Alias Type
raise NotImplementedError
@property
def send_action(self):
# Our send function depending on 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.
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = self.serializer_class(data=request.data)
if serializer.is_valid(raise_exception=True):
# Validate -
user = serializer.validated_data['user']
# Create callback token for sending alias type
token = create_callback_token_for_user(user, self.alias_type)
# Send to alias
success = self.send_action(user, token, self.message_payload)
# Respond With Success Or Failure of Sent
if success:
status_code = status.HTTP_200_OK
response_detail = self.success_response
else:
status_code = status.HTTP_400_BAD_REQUEST
response_detail = self.failure_response
return Response({'detail': response_detail}, status=status_code)
else:
return Response(serializer.error_messages, status=status.HTTP_400_BAD_REQUEST)
class ObtainEmailCallbackToken(AbstractBaseObtainCallbackToken):
serializer_class = EmailAuthSerializer
send_action = send_email_with_callback_token
success_response = "A login token has been sent to your email."
failure_response = "Unable to email you a login code. Try again later."
alias_type = 'email'
email_subject = api_settings.PASSWORDLESS_EMAIL_SUBJECT
email_plaintext = api_settings.PASSWORDLESS_EMAIL_PLAINTEXT_MESSAGE
email_html = api_settings.PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE_NAME
message_payload = {'email_subject': email_subject,
'email_plaintext': email_plaintext,
'email_html': email_html}
class ObtainMobileCallbackToken(AbstractBaseObtainCallbackToken):
serializer_class = MobileAuthSerializer
send_action = send_sms_with_callback_token
success_response = "We texted you a login code."
failure_response = "Unable to send you a login code. Try again later."
alias_type = 'mobile'
mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE
message_payload = {'mobile_message': mobile_message}
class ObtainEmailVerificationCallbackToken(AbstractBaseObtainCallbackToken):
serializer_class = EmailAuthSerializer
send_action = send_email_with_callback_token
success_response = "A verification token has been sent to your email."
failure_response = "Unable to email you a verification code. Try again later."
alias_type = 'email'
email_subject = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT
email_plaintext = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE
email_html = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_TOKEN_HTML_TEMPLATE_NAME
message_payload = {'email_subject': email_subject,
'email_plaintext': email_plaintext,
'email_html': email_html}
class ObtainMobileVerificationCallbackToken(AbstractBaseObtainCallbackToken):
serializer_class = MobileAuthSerializer
send_action = send_sms_with_callback_token
success_response = "We texted you a verification code."
failure_response = "Unable to send you a verification code. Try again later."
alias_type = 'mobile'
mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE
message_payload = {'mobile_message': mobile_message}
class AbstractBaseObtainAuthToken(APIView):
"""
This is a duplicate of rest_framework's own ObtainAuthToken method.
Instead, this returns an Auth Token based on our 6 digit callback token and source.
"""
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = None
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid(raise_exception=True):
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
if created:
# Initially set an unusable password if a user is created through this.
user.set_unusable_password()
user.save()
if token:
# Return our key for consumption.
return Response({'token': token.key}, status=status.HTTP_200_OK)
else:
log.error(
"Couldn't log in unknown user. Errors on serializer: %s" % (serializer.error_messages, ))
return Response({'detail': 'Couldn\'t log you in. Try again later.'},
status=status.HTTP_400_BAD_REQUEST)
class ObtainAuthTokenFromCallbackToken(AbstractBaseObtainAuthToken):
"""
This is a duplicate of rest_framework's own ObtainAuthToken method.
Instead, this returns an Auth Token based on our callback token and source.
"""
serializer_class = CallbackTokenAuthSerializer
class VerifyAliasFromCallbackToken(APIView):
"""
This verifies an alias on correct callback token entry using the same logic as auth.
Should be refactored at some point.
"""
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = CallbackTokenVerificationSerializer
def post(self, request, *args, **kwargs):
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:
log.error(
"Couldn't verify unknown user. Errors on serializer: %s" % (serializer.error_messages, ))
return Response({'detail': 'We couldn\'t verify this alias. Try again later.'}, status.HTTP_400_BAD_REQUEST)