Skip to content

Commit 75c12f4

Browse files
committed
Start work
1 parent 59a1636 commit 75c12f4

11 files changed

Lines changed: 344 additions & 1 deletion

File tree

Pipfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[[source]]
2+
name = "pypi"
3+
url = "https://pypi.org/simple"
4+
verify_ssl = true
5+
6+
[dev-packages]
7+
tox = "*"
8+
coverage = "*"
9+
djangorestframework = "*"
10+
11+
[packages]
12+
13+
[requires]
14+
python_version = "3.7"

Pipfile.lock

Lines changed: 116 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

simple_email_confirmation/contrib/__init__.py

Whitespace-only changes.

simple_email_confirmation/contrib/rest_framework/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from rest_framework import permissions
2+
3+
4+
class EmailViewSetPermission(permissions.IsAuthenticated):
5+
"""
6+
The request is authenticated as a user, or is a request to the
7+
confirm endpoint.
8+
"""
9+
10+
def has_permission(self, request, view):
11+
if view.action == 'confirm':
12+
return True
13+
14+
return super().has_permission(request, view)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from rest_framework import serializers
2+
3+
from simple_email_confirmation import get_email_address_model
4+
5+
6+
class EmailAddressSerializer(serializers.ModelSerializer):
7+
8+
class Meta:
9+
model = get_email_address_model()
10+
fields = (
11+
'pk',
12+
'email',
13+
'is_confirmed',
14+
'is_primary',
15+
)
16+
17+
18+
class ConfirmEmailAddressSerializer(serializers.Serializer):
19+
id = serializers.UUIDField()
20+
code = serializers.CharField()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from rest_framework.routers import SimpleRouter
2+
3+
from . import views
4+
5+
6+
router = SimpleRouter()
7+
8+
router.register(
9+
r'auth/emails',
10+
views.EmailViewSet,
11+
basename='user-emails',
12+
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class CurrentUserMixin:
2+
def get_queryset(self):
3+
queryset = super().get_queryset()
4+
5+
# Get configuration
6+
current_user = self.request.user
7+
user_field = self.user_field
8+
9+
return queryset.filter(**{
10+
user_field: current_user
11+
})
12+
13+
14+
class ActionSerializerMixin:
15+
action_serializers = {}
16+
17+
def get_action_serializers(self):
18+
return self.action_serializers
19+
20+
def get_serializer_class(self):
21+
if self.action in self.get_action_serializers():
22+
return self.action_serializers.get(self.action, None)
23+
24+
else:
25+
return super().get_serializer_class()
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from django.db import IntegrityError
2+
from django.shortcuts import get_object_or_404
3+
from rest_framework import viewsets, status
4+
from rest_framework.decorators import action
5+
from rest_framework.response import Response
6+
7+
from simple_email_confirmation import get_email_address_model
8+
from simple_email_confirmation.exceptions import (
9+
EmailConfirmationExpired, EmailIsPrimary, EmailNotConfirmed,
10+
)
11+
12+
from . import permissions, serializers, utils
13+
14+
15+
class EmailViewSet(
16+
utils.ActionSerializerMixin,
17+
utils.CurrentUserMixin,
18+
viewsets.ModelViewSet,
19+
):
20+
email_address_model = get_email_address_model()
21+
queryset = email_address_model.objects.all()
22+
user_field = 'user'
23+
permission_classes = [permissions.EmailViewSetPermission]
24+
25+
serializer_class = serializers.EmailAddressSerializer
26+
action_serializers = {
27+
'send_confirmation': None,
28+
'confirm': serializers.ConfirmEmailAddressSerializer,
29+
'set_primary': None,
30+
}
31+
32+
def create(self, request, *args, **kwargs):
33+
try:
34+
# Add the new unconfirmed email
35+
request.user.add_unconfirmed_email(request.data['email'])
36+
37+
# Retrieve the new email
38+
new_email = request.user.email_address_set.filter(
39+
email=request.data['email']
40+
).first()
41+
42+
# Return serialized email
43+
serializer = self.get_serializer(new_email)
44+
return Response(serializer.data)
45+
46+
except IntegrityError:
47+
return Response(
48+
{'errors': ['Email already exists']},
49+
status=status.HTTP_400_BAD_REQUEST,
50+
)
51+
52+
def destroy(self, request, *args, **kwargs):
53+
email = self.get_object()
54+
55+
try:
56+
# Delete the email
57+
request.user.remove_email(email.email)
58+
59+
return Response({'status': 'OK'})
60+
61+
except EmailIsPrimary:
62+
return Response(
63+
{'errors': ['Cannot delete the primary email']},
64+
status=status.HTTP_400_BAD_REQUEST,
65+
)
66+
67+
@action(methods=['post'], detail=True)
68+
def send_confirmation(self, request, pk=None):
69+
"""
70+
Send a confirmation email.
71+
"""
72+
73+
# Retrive email
74+
email = self.get_object()
75+
76+
# Confirm email
77+
email.user.reset_email_confirmation(email.email)
78+
email.user.send_confirmation_email(email.email)
79+
80+
# Success!
81+
return Response({'status': 'OK'})
82+
83+
@action(methods=['post'], detail=False)
84+
def confirm(self, request):
85+
"""
86+
Confirm an email.
87+
"""
88+
89+
try:
90+
# Parse data
91+
serializer = self.get_serializer(data=request.data)
92+
serializer.is_valid(raise_exception=True)
93+
94+
# Retrieve email
95+
email = get_object_or_404(
96+
self.email_address_model,
97+
pk=serializer.data['id'],
98+
)
99+
100+
# Retrieve user
101+
email.user.confirm_email(serializer.data['code'])
102+
103+
# Success!
104+
return Response({'status': 'OK'})
105+
106+
except EmailConfirmationExpired:
107+
return Response(
108+
{'errors': ['Confirmation key is not valid']},
109+
status=status.HTTP_400_BAD_REQUEST,
110+
)
111+
112+
@action(methods=['post'], detail=True)
113+
def set_primary(self, request, pk=None):
114+
"""
115+
Set an email as primary.
116+
"""
117+
118+
# Retrive email
119+
email = self.get_object()
120+
121+
try:
122+
# Set email as primary
123+
email.user.set_primary_email(email.email)
124+
125+
# Success!
126+
return Response({'status': 'OK'})
127+
128+
except EmailNotConfirmed:
129+
return Response(
130+
{'errors': [
131+
'Email must be confirmed before it can be set as primary'
132+
]},
133+
status=status.HTTP_400_BAD_REQUEST,
134+
)
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
urlpatterns = []
1+
from django.urls import path, include
2+
3+
from simple_email_confirmation.contrib.rest_framework.urls import router
4+
5+
6+
urlpatterns = [
7+
path('api/', include(router.urls)),
8+
]

0 commit comments

Comments
 (0)