2024-04-16 17:06:43 +02:00
|
|
|
"""API endpoints"""
|
|
|
|
|
|
2024-09-14 00:59:38 +02:00
|
|
|
from django.db.models import Subquery
|
|
|
|
|
|
2024-09-27 15:54:44 +02:00
|
|
|
from rest_framework import exceptions, filters, mixins, viewsets
|
2024-11-22 19:49:27 +01:00
|
|
|
from rest_framework.decorators import action
|
|
|
|
|
from rest_framework.response import Response
|
2024-04-16 17:06:43 +02:00
|
|
|
|
2024-04-17 11:19:22 +02:00
|
|
|
from core import models as core_models
|
2024-04-16 17:06:43 +02:00
|
|
|
|
2024-09-27 15:54:44 +02:00
|
|
|
from mailbox_manager import enums, models
|
2024-11-25 14:44:34 +01:00
|
|
|
from mailbox_manager.api import permissions
|
|
|
|
|
from mailbox_manager.api.client import serializers
|
2024-11-22 19:49:27 +01:00
|
|
|
from mailbox_manager.utils.dimail import DimailAPIClient
|
2024-04-16 17:06:43 +02:00
|
|
|
|
|
|
|
|
|
2024-04-17 11:19:22 +02:00
|
|
|
# pylint: disable=too-many-ancestors
|
2024-04-16 17:06:43 +02:00
|
|
|
class MailDomainViewSet(
|
2024-04-17 11:19:22 +02:00
|
|
|
mixins.CreateModelMixin,
|
2024-04-16 17:06:43 +02:00
|
|
|
mixins.ListModelMixin,
|
2024-04-17 11:19:22 +02:00
|
|
|
mixins.RetrieveModelMixin,
|
2024-04-16 17:06:43 +02:00
|
|
|
viewsets.GenericViewSet,
|
|
|
|
|
):
|
2024-04-17 11:19:22 +02:00
|
|
|
"""
|
|
|
|
|
MailDomain viewset.
|
2024-04-16 17:06:43 +02:00
|
|
|
|
2024-04-17 11:19:22 +02:00
|
|
|
GET /api/<version>/mail-domains/
|
|
|
|
|
Return a list of mail domains user has access to.
|
|
|
|
|
|
2024-06-03 16:59:55 +02:00
|
|
|
GET /api/<version>/mail-domains/<domain-slug>/
|
2024-04-17 11:19:22 +02:00
|
|
|
Return details for a mail domain user has access to.
|
|
|
|
|
|
|
|
|
|
POST /api/<version>/mail-domains/ with expected data:
|
|
|
|
|
- name: str
|
|
|
|
|
Return newly created domain
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [permissions.AccessPermission]
|
2024-04-16 17:06:43 +02:00
|
|
|
serializer_class = serializers.MailDomainSerializer
|
2024-04-17 11:19:22 +02:00
|
|
|
filter_backends = [filters.OrderingFilter]
|
|
|
|
|
ordering_fields = ["created_at", "name"]
|
|
|
|
|
ordering = ["-created_at"]
|
2024-06-03 16:59:55 +02:00
|
|
|
lookup_field = "slug"
|
2024-04-16 17:06:43 +02:00
|
|
|
queryset = models.MailDomain.objects.all()
|
|
|
|
|
|
2024-04-17 11:19:22 +02:00
|
|
|
def get_queryset(self):
|
2024-10-25 14:59:02 +02:00
|
|
|
"""Restrict results to the current user's team."""
|
2024-04-17 11:19:22 +02:00
|
|
|
return self.queryset.filter(accesses__user=self.request.user)
|
|
|
|
|
|
|
|
|
|
def perform_create(self, serializer):
|
|
|
|
|
"""Set the current user as owner of the newly created mail domain."""
|
|
|
|
|
|
|
|
|
|
domain = serializer.save()
|
2024-11-20 19:14:48 +01:00
|
|
|
serializers.MailDomainAccessSerializer().create(
|
|
|
|
|
validated_data={
|
|
|
|
|
"user": self.request.user,
|
|
|
|
|
"domain": domain,
|
|
|
|
|
"role": str(core_models.RoleChoices.OWNER),
|
|
|
|
|
}
|
2024-04-17 11:19:22 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# pylint: disable=too-many-ancestors
|
|
|
|
|
class MailDomainAccessViewSet(
|
|
|
|
|
viewsets.GenericViewSet,
|
2024-09-14 00:59:38 +02:00
|
|
|
mixins.ListModelMixin,
|
2024-09-25 00:43:02 +02:00
|
|
|
mixins.CreateModelMixin,
|
2024-09-27 15:54:44 +02:00
|
|
|
mixins.UpdateModelMixin,
|
2024-09-14 00:59:38 +02:00
|
|
|
mixins.RetrieveModelMixin,
|
2024-09-30 14:35:53 +02:00
|
|
|
mixins.DestroyModelMixin,
|
2024-04-17 11:19:22 +02:00
|
|
|
):
|
|
|
|
|
"""
|
2024-09-14 00:59:38 +02:00
|
|
|
API ViewSet for all interactions with mail domain accesses.
|
|
|
|
|
|
|
|
|
|
GET /api/v1.0/mail-domains/<domain_slug>/accesses/:<domain_access_id>
|
|
|
|
|
Return list of all domain accesses related to the logged-in user and one
|
|
|
|
|
domain access if an id is provided.
|
2024-09-27 15:54:44 +02:00
|
|
|
|
2024-09-25 00:43:02 +02:00
|
|
|
POST /api/v1.0/mail-domains/<domain_slug>/accesses/ with expected data:
|
|
|
|
|
- user: str
|
|
|
|
|
- role: str [owner|admin|viewer]
|
|
|
|
|
Return newly created mail domain access
|
|
|
|
|
|
2024-09-27 15:54:44 +02:00
|
|
|
PUT /api/v1.0/mail-domains/<domain_slug>/accesses/<domain_access_id>/ with expected data:
|
|
|
|
|
- role: str [owner|admin|viewer]
|
|
|
|
|
Return updated domain access
|
|
|
|
|
|
|
|
|
|
PATCH /api/v1.0/mail-domains/<domain_slug>/accesses/<domain_access_id>/ with expected data:
|
|
|
|
|
- role: str [owner|admin|viewer]
|
|
|
|
|
Return partially updated domain access
|
2024-09-30 14:35:53 +02:00
|
|
|
|
|
|
|
|
DELETE /api/v1.0/mail-domains/<domain_slug>/accesses/<domain_access_id>/
|
|
|
|
|
Delete targeted domain access
|
2024-04-17 11:19:22 +02:00
|
|
|
"""
|
|
|
|
|
|
2024-09-30 14:35:53 +02:00
|
|
|
permission_classes = [permissions.MailDomainAccessRolePermission]
|
2024-04-17 11:19:22 +02:00
|
|
|
serializer_class = serializers.MailDomainAccessSerializer
|
|
|
|
|
filter_backends = [filters.OrderingFilter]
|
2024-09-14 00:59:38 +02:00
|
|
|
ordering_fields = ["role", "user__email", "user__name"]
|
2024-04-17 11:19:22 +02:00
|
|
|
ordering = ["-created_at"]
|
2024-09-14 00:59:38 +02:00
|
|
|
queryset = (
|
|
|
|
|
models.MailDomainAccess.objects.all()
|
|
|
|
|
.select_related("user")
|
|
|
|
|
.order_by("-created_at")
|
|
|
|
|
)
|
|
|
|
|
list_serializer_class = serializers.MailDomainAccessReadOnlySerializer
|
|
|
|
|
detail_serializer_class = serializers.MailDomainAccessSerializer
|
|
|
|
|
|
|
|
|
|
def get_serializer_class(self):
|
2024-10-25 14:59:02 +02:00
|
|
|
"""Chooses list or detail serializer according to the action."""
|
2024-09-14 00:59:38 +02:00
|
|
|
if self.action in {"list", "retrieve"}:
|
|
|
|
|
return self.list_serializer_class
|
|
|
|
|
return self.detail_serializer_class
|
|
|
|
|
|
|
|
|
|
def get_serializer_context(self):
|
|
|
|
|
"""Extra context provided to the serializer class."""
|
|
|
|
|
context = super().get_serializer_context()
|
|
|
|
|
context["domain_slug"] = self.kwargs["domain_slug"]
|
2024-09-27 15:54:44 +02:00
|
|
|
context["authenticated_user"] = self.request.user
|
2024-09-14 00:59:38 +02:00
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
"""Return the queryset according to the action."""
|
|
|
|
|
queryset = super().get_queryset()
|
|
|
|
|
queryset = queryset.filter(domain__slug=self.kwargs["domain_slug"])
|
|
|
|
|
|
|
|
|
|
if self.action in {"list", "retrieve"}:
|
|
|
|
|
# Determine which role the logged-in user has in the domain
|
|
|
|
|
user_role_query = models.MailDomainAccess.objects.filter(
|
|
|
|
|
user=self.request.user, domain__slug=self.kwargs["domain_slug"]
|
|
|
|
|
).values("role")[:1]
|
|
|
|
|
|
|
|
|
|
queryset = (
|
|
|
|
|
# The logged-in user should be part of a domain to see its accesses
|
|
|
|
|
queryset.filter(
|
|
|
|
|
domain__accesses__user=self.request.user,
|
|
|
|
|
)
|
|
|
|
|
# Abilities are computed based on logged-in user's role and
|
|
|
|
|
# the user role on each domain access
|
|
|
|
|
.annotate(
|
|
|
|
|
user_role=Subquery(user_role_query),
|
|
|
|
|
)
|
|
|
|
|
.select_related("user")
|
|
|
|
|
.distinct()
|
|
|
|
|
)
|
|
|
|
|
return queryset
|
2024-04-17 11:19:22 +02:00
|
|
|
|
2024-09-27 15:54:44 +02:00
|
|
|
def perform_update(self, serializer):
|
|
|
|
|
"""Check that we don't change the role if it leads to losing the last owner."""
|
|
|
|
|
instance = serializer.instance
|
|
|
|
|
|
|
|
|
|
# Check if the role is being updated and the new role is not "owner"
|
|
|
|
|
if (
|
|
|
|
|
"role" in self.request.data
|
|
|
|
|
and self.request.data["role"] != enums.MailDomainRoleChoices.OWNER
|
|
|
|
|
):
|
|
|
|
|
domain = instance.domain
|
|
|
|
|
# Check if the access being updated is the last owner access for the domain
|
|
|
|
|
if (
|
|
|
|
|
instance.role == enums.MailDomainRoleChoices.OWNER
|
|
|
|
|
and domain.accesses.filter(
|
|
|
|
|
role=enums.MailDomainRoleChoices.OWNER
|
|
|
|
|
).count()
|
|
|
|
|
== 1
|
|
|
|
|
):
|
|
|
|
|
message = "Cannot change the role to a non-owner role for the last owner access."
|
|
|
|
|
raise exceptions.PermissionDenied({"role": message})
|
|
|
|
|
serializer.save()
|
|
|
|
|
|
2024-09-30 14:35:53 +02:00
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
|
|
|
"""Forbid deleting the last owner access"""
|
|
|
|
|
instance = self.get_object()
|
|
|
|
|
domain = instance.domain
|
|
|
|
|
|
|
|
|
|
# Check if the access being deleted is the last owner access for the domain
|
|
|
|
|
if (
|
|
|
|
|
instance.role == enums.MailDomainRoleChoices.OWNER
|
|
|
|
|
and domain.accesses.filter(role=enums.MailDomainRoleChoices.OWNER).count()
|
|
|
|
|
== 1
|
|
|
|
|
):
|
|
|
|
|
message = "Cannot delete the last owner access for the domain."
|
|
|
|
|
raise exceptions.PermissionDenied({"detail": message})
|
|
|
|
|
|
|
|
|
|
return super().destroy(request, *args, **kwargs)
|
|
|
|
|
|
2024-04-16 17:06:43 +02:00
|
|
|
|
|
|
|
|
class MailBoxViewSet(
|
|
|
|
|
mixins.CreateModelMixin,
|
|
|
|
|
mixins.ListModelMixin,
|
|
|
|
|
viewsets.GenericViewSet,
|
|
|
|
|
):
|
2024-08-05 12:20:44 +02:00
|
|
|
"""MailBox ViewSet
|
|
|
|
|
|
2024-11-22 19:49:27 +01:00
|
|
|
GET /api/<version>/mail-domains/<domain_slug>/mailboxes/
|
2024-08-05 12:20:44 +02:00
|
|
|
Return a list of mailboxes on the domain
|
|
|
|
|
|
2024-11-22 19:49:27 +01:00
|
|
|
POST /api/<version>/mail-domains/<domain_slug>/mailboxes/ with expected data:
|
2024-08-05 12:20:44 +02:00
|
|
|
- first_name: str
|
|
|
|
|
- last_name: str
|
|
|
|
|
- local_part: str
|
|
|
|
|
- secondary_email: str
|
|
|
|
|
Sends request to email provisioning API and returns newly created mailbox
|
2024-11-22 19:49:27 +01:00
|
|
|
|
|
|
|
|
POST /api/<version>/mail-domains/<domain_slug>/mailboxes/<mailbox_id>/disable/
|
|
|
|
|
Send a request to dimail to disable mailbox and change status of the mailbox in our DB
|
2024-11-26 12:26:54 +01:00
|
|
|
|
|
|
|
|
POST /api/<version>/mail-domains/<domain_slug>/mailboxes/<mailbox_id>/enable/
|
|
|
|
|
Send a request to dimail to enable mailbox and change status of the mailbox in our DB
|
2024-08-05 12:20:44 +02:00
|
|
|
"""
|
2024-04-16 17:06:43 +02:00
|
|
|
|
2024-08-06 00:04:51 +02:00
|
|
|
permission_classes = [permissions.MailBoxPermission]
|
2024-04-16 17:06:43 +02:00
|
|
|
serializer_class = serializers.MailboxSerializer
|
2024-08-06 00:04:51 +02:00
|
|
|
filter_backends = [filters.OrderingFilter]
|
|
|
|
|
ordering = ["-created_at"]
|
2024-04-16 17:06:43 +02:00
|
|
|
queryset = models.Mailbox.objects.all()
|
|
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
"""Custom queryset to get mailboxes related to a mail domain."""
|
2024-06-03 16:59:55 +02:00
|
|
|
domain_slug = self.kwargs.get("domain_slug", "")
|
|
|
|
|
if domain_slug:
|
|
|
|
|
return self.queryset.filter(domain__slug=domain_slug)
|
2024-04-16 17:06:43 +02:00
|
|
|
return self.queryset
|
|
|
|
|
|
|
|
|
|
def perform_create(self, serializer):
|
|
|
|
|
"""Create new mailbox."""
|
2024-06-03 16:59:55 +02:00
|
|
|
domain_slug = self.kwargs.get("domain_slug", "")
|
|
|
|
|
if domain_slug:
|
2024-04-16 17:06:43 +02:00
|
|
|
serializer.validated_data["domain"] = models.MailDomain.objects.get(
|
2024-06-03 16:59:55 +02:00
|
|
|
slug=domain_slug
|
2024-04-16 17:06:43 +02:00
|
|
|
)
|
|
|
|
|
super().perform_create(serializer)
|
2024-11-22 19:49:27 +01:00
|
|
|
|
|
|
|
|
@action(detail=True, methods=["post"])
|
|
|
|
|
def disable(self, request, domain_slug, pk=None): # pylint: disable=unused-argument
|
|
|
|
|
"""Disable mailbox. Send a request to dimail and change status in our DB"""
|
|
|
|
|
mailbox = self.get_object()
|
|
|
|
|
client = DimailAPIClient()
|
|
|
|
|
client.disable_mailbox(mailbox, request.user.sub)
|
|
|
|
|
mailbox.status = enums.MailboxStatusChoices.DISABLED
|
|
|
|
|
mailbox.save()
|
|
|
|
|
return Response(serializers.MailboxSerializer(mailbox).data)
|
2024-11-26 12:26:54 +01:00
|
|
|
|
|
|
|
|
@action(detail=True, methods=["post"])
|
|
|
|
|
def enable(self, request, domain_slug, pk=None): # pylint: disable=unused-argument
|
|
|
|
|
"""Enable mailbox. Send a request to dimail and change status in our DB"""
|
|
|
|
|
mailbox = self.get_object()
|
|
|
|
|
client = DimailAPIClient()
|
|
|
|
|
client.enable_mailbox(mailbox, request.user.sub)
|
|
|
|
|
mailbox.status = enums.MailboxStatusChoices.ENABLED
|
|
|
|
|
mailbox.save()
|
|
|
|
|
return Response(serializers.MailboxSerializer(mailbox).data)
|