views.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. from allauth.account.models import EmailAddress
  2. from dj_rest_auth.registration.views import (
  3. SocialAccountDisconnectView as BaseSocialAccountDisconnectView,
  4. )
  5. from django.core.exceptions import ObjectDoesNotExist, ValidationError
  6. from django.http import Http404
  7. from django.shortcuts import get_object_or_404
  8. from drf_yasg.utils import swagger_auto_schema
  9. from rest_framework import exceptions, mixins, status, viewsets
  10. from rest_framework.decorators import action
  11. from rest_framework.response import Response
  12. from organizations_ext.models import Organization
  13. from projects.models import UserProjectAlert
  14. from users.utils import is_user_registration_open
  15. from .models import User
  16. from .serializers import (
  17. ConfirmEmailAddressSerializer,
  18. CurrentUserSerializer,
  19. EmailAddressSerializer,
  20. UserNotificationsSerializer,
  21. UserSerializer,
  22. )
  23. class UserViewSet(viewsets.ModelViewSet):
  24. queryset = User.objects.all()
  25. serializer_class = UserSerializer
  26. def get_queryset(self):
  27. queryset = super().get_queryset()
  28. organization_slug = self.kwargs.get("organization_slug")
  29. if organization_slug:
  30. queryset = queryset.filter(
  31. organizations_ext_organization__slug=organization_slug,
  32. organizations_ext_organization__users=self.request.user,
  33. )
  34. else:
  35. queryset = queryset.filter(id=self.request.user.id)
  36. return queryset
  37. def get_object(self):
  38. if self.kwargs.get("pk") == "me":
  39. return self.request.user
  40. return super().get_object()
  41. def get_serializer_class(self):
  42. if self.kwargs.get("pk") == "me":
  43. return CurrentUserSerializer
  44. return super().get_serializer_class()
  45. def perform_create(self, serializer):
  46. organization_slug = self.kwargs.get("organization_slug")
  47. try:
  48. Organization.objects.get(slug=organization_slug)
  49. except Organization.DoesNotExist as err:
  50. raise exceptions.ValidationError("Organization does not exist") from err
  51. # TODO deal with organization and users who aren't set up yet
  52. if not is_user_registration_open() and not self.request.user.is_superuser:
  53. raise exceptions.PermissionDenied("Registration is not open")
  54. user = serializer.save()
  55. return user
  56. def destroy(self, request, *args, **kwargs):
  57. instance = self.get_object()
  58. if instance.organizations_ext_organizationuser.filter(
  59. organizationowner__isnull=False
  60. ):
  61. return Response(
  62. data={
  63. "message": "User is organization owner. Delete organization or transfer ownership first."
  64. },
  65. status=status.HTTP_400_BAD_REQUEST,
  66. )
  67. self.perform_destroy(instance)
  68. return Response(status=status.HTTP_204_NO_CONTENT)
  69. @action(detail=True, methods=["get", "post", "put"])
  70. def notifications(self, request, pk=None):
  71. user = self.get_object()
  72. if request.method == "GET":
  73. serializer = UserNotificationsSerializer(user)
  74. return Response(serializer.data)
  75. serializer = UserNotificationsSerializer(user, data=request.data)
  76. if serializer.is_valid():
  77. serializer.save()
  78. return Response(serializer.data)
  79. else:
  80. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  81. @action(
  82. detail=True, methods=["get", "post", "put"], url_path="notifications/alerts"
  83. )
  84. def alerts(self, request, pk=None):
  85. """
  86. Returns dictionary of project_id: status. Now project_id status means it's "default"
  87. To update, submit `{project_id: status}` where status is -1 (default), 0, or 1
  88. """
  89. user = self.get_object()
  90. alerts = user.userprojectalert_set.all()
  91. if request.method == "GET":
  92. data = {}
  93. for alert in alerts:
  94. data[alert.project_id] = alert.status
  95. return Response(data)
  96. data = request.data
  97. try:
  98. items = [x for x in data.items()]
  99. except AttributeError as err:
  100. raise exceptions.ValidationError(
  101. "Invalid alert format, expected dictionary"
  102. ) from err
  103. if len(data) != 1:
  104. raise exceptions.ValidationError("Invalid alert format, expected one value")
  105. project_id, alert_status = items[0]
  106. if alert_status not in [1, 0, -1]:
  107. raise exceptions.ValidationError("Invalid status, must be -1, 0, or 1")
  108. alert = alerts.filter(project_id=project_id).first()
  109. if alert and alert_status == -1:
  110. alert.delete()
  111. else:
  112. UserProjectAlert.objects.update_or_create(
  113. user=user, project_id=project_id, defaults={"status": alert_status}
  114. )
  115. return Response(status=204)
  116. class EmailAddressViewSet(
  117. mixins.CreateModelMixin,
  118. mixins.ListModelMixin,
  119. viewsets.GenericViewSet,
  120. ):
  121. queryset = EmailAddress.objects.all()
  122. serializer_class = EmailAddressSerializer
  123. pagination_class = None
  124. def get_user(self, user_pk):
  125. if user_pk == "me":
  126. return self.request.user
  127. raise exceptions.ValidationError(
  128. "Can only change primary email address on own account"
  129. )
  130. def get_queryset(self):
  131. if getattr(self, "swagger_fake_view", False):
  132. # queryset just for schema generation metadata
  133. return EmailAddress.objects.none()
  134. user = self.get_user(self.kwargs.get("user_pk"))
  135. queryset = super().get_queryset().filter(user=user)
  136. return queryset
  137. def put(self, request, user_pk, format=None):
  138. """
  139. Set a new primary email (must be verified) this will also set the email used when a user logs in.
  140. """
  141. user = self.get_user(user_pk)
  142. try:
  143. email_address = user.emailaddress_set.get(
  144. email=request.data.get("email"), verified=True
  145. )
  146. email_address.set_as_primary()
  147. except ObjectDoesNotExist as err:
  148. raise Http404 from err
  149. serializer = self.serializer_class(
  150. instance=email_address, data=request.data, context={"request": request}
  151. )
  152. if serializer.is_valid():
  153. serializer.save()
  154. return Response(serializer.data)
  155. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  156. def delete(self, request, user_pk, format=None):
  157. user = self.get_user(user_pk)
  158. try:
  159. email_address = user.emailaddress_set.get(
  160. email=request.data.get("email"), primary=False
  161. )
  162. except ObjectDoesNotExist as err:
  163. raise Http404 from err
  164. email_address.delete()
  165. return Response(status=status.HTTP_204_NO_CONTENT)
  166. @swagger_auto_schema(responses={204: "No Content"})
  167. @action(detail=False, methods=["post"])
  168. def confirm(self, request, user_pk):
  169. serializer = ConfirmEmailAddressSerializer(data=request.data)
  170. serializer.is_valid(raise_exception=True)
  171. email_address = get_object_or_404(
  172. self.get_queryset(), email=serializer.validated_data.get("email")
  173. )
  174. email_address.send_confirmation(request)
  175. return Response(status=204)
  176. class SocialAccountDisconnectView(BaseSocialAccountDisconnectView):
  177. def post(self, request, *args, **kwargs):
  178. try:
  179. return super().post(request, *args, **kwargs)
  180. except ValidationError as e:
  181. raise exceptions.ValidationError(e.message)