Skip to content

Commit dd1db0a

Browse files
committed
Add Cyberstorm endpoint for disconnecting linked accounts
1 parent 86f7377 commit dd1db0a

File tree

3 files changed

+102
-11
lines changed

3 files changed

+102
-11
lines changed

django/thunderstore/api/cyberstorm/views/user.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
from django.http import HttpRequest
2-
from rest_framework import serializers
3-
from rest_framework.exceptions import ValidationError
2+
from django.utils.translation import gettext_lazy as _
3+
from rest_framework import serializers, status
4+
from rest_framework.exceptions import APIException, ValidationError
45
from rest_framework.permissions import IsAuthenticated
56
from rest_framework.response import Response
67
from rest_framework.views import APIView
78

89
from thunderstore.api.utils import conditional_swagger_auto_schema
9-
from thunderstore.social.views import DeleteAccountForm
10+
from thunderstore.social.views import (
11+
DeleteAccountForm,
12+
LinkedAccountDisconnectExecption,
13+
LinkedAccountDisconnectForm,
14+
)
15+
16+
17+
class CyberstormException(APIException):
18+
status_code = status.HTTP_400_BAD_REQUEST
19+
default_detail = _("Issue occured when trying to process action")
20+
default_code = "error"
1021

1122

1223
class CyberstormUserDeleteRequestSerialiazer(serializers.Serializer):
@@ -38,3 +49,47 @@ def post(self, request: HttpRequest):
3849
return Response()
3950
else:
4051
raise ValidationError(form.errors)
52+
53+
54+
class CyberstormUserDisconnectProviderRequestSerialiazer(serializers.Serializer):
55+
provider = serializers.CharField()
56+
57+
58+
class CyberstormUserDisconnectProviderResponseSerialiazer(serializers.Serializer):
59+
username = serializers.CharField()
60+
provider = serializers.CharField()
61+
62+
63+
class UserLinkedAccountDisconnectAPIView(APIView):
64+
permission_classes = [IsAuthenticated]
65+
66+
@conditional_swagger_auto_schema(
67+
request_body=CyberstormUserDisconnectProviderRequestSerialiazer,
68+
responses={200: CyberstormUserDisconnectProviderResponseSerialiazer},
69+
operation_id="cyberstorm.current-user.linked-account-disconnect",
70+
tags=["cyberstorm"],
71+
)
72+
def post(self, request: HttpRequest):
73+
serializer = CyberstormUserDisconnectProviderRequestSerialiazer(
74+
data=request.data
75+
)
76+
serializer.is_valid(raise_exception=True)
77+
form = LinkedAccountDisconnectForm(
78+
user=request.user,
79+
data=serializer.validated_data,
80+
)
81+
if form.is_valid():
82+
try:
83+
form.disconnect_account(with_raise=True)
84+
except LinkedAccountDisconnectExecption as e:
85+
raise CyberstormException(detail=e)
86+
return Response(
87+
CyberstormUserDisconnectProviderResponseSerialiazer(
88+
{
89+
"username": request.user.username,
90+
"provider": serializer.validated_data["provider"],
91+
}
92+
).data
93+
)
94+
else:
95+
raise ValidationError(form.errors)

django/thunderstore/api/urls.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
TeamMemberListAPIView,
2525
TeamServiceAccountListAPIView,
2626
)
27-
from thunderstore.api.cyberstorm.views.user import UserDeleteAPIView
27+
from thunderstore.api.cyberstorm.views.user import (
28+
UserDeleteAPIView,
29+
UserLinkedAccountDisconnectAPIView,
30+
)
2831

2932
cyberstorm_urls = [
3033
path(
@@ -142,6 +145,11 @@
142145
UserDeleteAPIView.as_view(),
143146
name="cyberstorm.current-user.delete",
144147
),
148+
path(
149+
"current-user/linked-account-disconnect/",
150+
UserLinkedAccountDisconnectAPIView.as_view(),
151+
name="cyberstorm.current-user.linked-account-disconnect",
152+
),
145153
path(
146154
"team/<str:team_name>/members/remove/",
147155
RemoveTeamMemberAPIView.as_view(),

django/thunderstore/social/views.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,43 @@
66
from thunderstore.frontend.views import SettingsViewMixin
77

88

9+
class LinkedAccountDisconnectExecption(Exception):
10+
"""Some problem with disconnecting a linked account"""
11+
12+
pass
13+
14+
915
class LinkedAccountDisconnectForm(forms.Form):
1016
provider = forms.CharField()
1117

18+
def __init__(self, *args, **kwargs):
19+
self.user = kwargs.pop("user")
20+
super().__init__(*args, **kwargs)
21+
22+
@property
23+
def can_disconnect(self):
24+
return self.user.social_auth.count() > 1
25+
26+
def clean_provider(self):
27+
data = self.cleaned_data["provider"]
28+
if data in ["github", "discord", "overwolf"]:
29+
return data
30+
else:
31+
raise forms.ValidationError("Invalid provider")
32+
33+
def disconnect_account(self, with_raise=False):
34+
if not self.can_disconnect:
35+
if with_raise:
36+
raise LinkedAccountDisconnectExecption(
37+
"User must have at least one linked account"
38+
)
39+
else:
40+
return
41+
social_auth = self.user.social_auth.filter(
42+
provider=self.cleaned_data["provider"]
43+
).first()
44+
social_auth.delete()
45+
1246

1347
class LinkedAccountsView(SettingsViewMixin, RequireAuthenticationMixin, FormView):
1448
template_name = "settings/linked_accounts.html"
@@ -25,14 +59,8 @@ def get_context_data(self, **kwargs):
2559
def can_disconnect(self):
2660
return self.request.user.social_auth.count() > 1
2761

28-
def disconnect_account(self, provider):
29-
if not self.can_disconnect:
30-
return
31-
social_auth = self.request.user.social_auth.filter(provider=provider).first()
32-
social_auth.delete()
33-
3462
def form_valid(self, form):
35-
self.disconnect_account(form.cleaned_data["provider"])
63+
form.disconnect_account()
3664
return super().form_valid(form)
3765

3866

0 commit comments

Comments
 (0)