Skip to content

Commit

Permalink
Add Cyberstorm endpoint for disconnecting linked accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksamies committed Aug 20, 2024
1 parent 2bff326 commit ed9f73e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
61 changes: 58 additions & 3 deletions django/thunderstore/api/cyberstorm/views/user.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
from django.http import HttpRequest
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers, status
from rest_framework.exceptions import APIException, ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from thunderstore.api.utils import conditional_swagger_auto_schema
from thunderstore.social.views import DeleteAccountForm
from thunderstore.social.views import (
DeleteAccountForm,
LinkedAccountDisconnectExecption,
LinkedAccountDisconnectForm,
)


class CyberstormException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = _("Issue occured when trying to process action")
default_code = "error"


class CyberstormUserDeleteRequestSerialiazer(serializers.Serializer):
Expand Down Expand Up @@ -38,3 +49,47 @@ def post(self, request: HttpRequest):
return Response()
else:
raise ValidationError(form.errors)


class CyberstormUserDisconnectProviderRequestSerialiazer(serializers.Serializer):
provider = serializers.CharField()


class CyberstormUserDisconnectProviderResponseSerialiazer(serializers.Serializer):
username = serializers.CharField()
provider = serializers.CharField()


class UserLinkedAccountDisconnectAPIView(APIView):
permission_classes = [IsAuthenticated]

@conditional_swagger_auto_schema(
request_body=CyberstormUserDisconnectProviderRequestSerialiazer,
responses={200: CyberstormUserDisconnectProviderResponseSerialiazer},
operation_id="cyberstorm.current-user.linked-account-disconnect",
tags=["cyberstorm"],
)
def post(self, request: HttpRequest):
serializer = CyberstormUserDisconnectProviderRequestSerialiazer(
data=request.data
)
serializer.is_valid(raise_exception=True)
form = LinkedAccountDisconnectForm(
user=request.user,
data=serializer.validated_data,
)
if form.is_valid():
try:
form.disconnect_account(with_raise=True)
except LinkedAccountDisconnectExecption as e:
raise CyberstormException(detail=e)
return Response(
CyberstormUserDisconnectProviderResponseSerialiazer(
{
"username": request.user.username,
"provider": serializer.validated_data["provider"],
}
).data
)
else:
raise ValidationError(form.errors)
10 changes: 9 additions & 1 deletion django/thunderstore/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
TeamMemberListAPIView,
TeamServiceAccountListAPIView,
)
from thunderstore.api.cyberstorm.views.user import UserDeleteAPIView
from thunderstore.api.cyberstorm.views.user import (
UserDeleteAPIView,
UserLinkedAccountDisconnectAPIView,
)

cyberstorm_urls = [
path(
Expand Down Expand Up @@ -142,6 +145,11 @@
UserDeleteAPIView.as_view(),
name="cyberstorm.current-user.delete",
),
path(
"current-user/linked-account-disconnect/",
UserLinkedAccountDisconnectAPIView.as_view(),
name="cyberstorm.current-user.linked-account-disconnect",
),
path(
"team/<str:team_name>/members/remove/",
RemoveTeamMemberAPIView.as_view(),
Expand Down
42 changes: 35 additions & 7 deletions django/thunderstore/social/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,43 @@
from thunderstore.frontend.views import SettingsViewMixin


class LinkedAccountDisconnectExecption(Exception):
"""Some problem with disconnecting a linked account"""

pass


class LinkedAccountDisconnectForm(forms.Form):
provider = forms.CharField()

def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
super().__init__(*args, **kwargs)

@property
def can_disconnect(self):
return self.user.social_auth.count() > 1

def clean_provider(self):
data = self.cleaned_data["provider"]
if data in ["github", "discord", "overwolf"]:
return data
else:
raise forms.ValidationError("Invalid provider")

def disconnect_account(self, with_raise=False):
if not self.can_disconnect:
if with_raise:
raise LinkedAccountDisconnectExecption(
"User must have at least one linked account"
)
else:
return
social_auth = self.user.social_auth.filter(
provider=self.cleaned_data["provider"]
).first()
social_auth.delete()


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

def disconnect_account(self, provider):
if not self.can_disconnect:
return
social_auth = self.request.user.social_auth.filter(provider=provider).first()
social_auth.delete()

def form_valid(self, form):
self.disconnect_account(form.cleaned_data["provider"])
form.disconnect_account()
return super().form_valid(form)


Expand Down

0 comments on commit ed9f73e

Please sign in to comment.