Skip to content

Commit

Permalink
✨(backend) domain accesses list API
Browse files Browse the repository at this point in the history
Add an endpoint to list all accesses created for a domain
Return all roles available to set for each access depending to
the authenticated user.
  • Loading branch information
sdemagny committed Sep 17, 2024
1 parent cc86a3b commit dd8bd2a
Show file tree
Hide file tree
Showing 5 changed files with 482 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to

## [Unreleased]

✨(domains) add endpoint API to list all accesses created for a domain


## [1.1.0] - 2024-09-10

### Added
Expand Down
52 changes: 47 additions & 5 deletions src/backend/mailbox_manager/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from rest_framework import serializers

from mailbox_manager import models
from core.api.serializers import UserSerializer

from mailbox_manager import enums, models


class MailboxSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -50,15 +52,55 @@ def get_abilities(self, domain) -> dict:


class MailDomainAccessSerializer(serializers.ModelSerializer):
"""Serialize mail domain accesses."""
"""Serialize mail domain access."""

user = UserSerializer(read_only=True, fields=["id", "name", "email"])
can_set_role_to = serializers.SerializerMethodField(read_only=True)

class Meta:
model = models.MailDomainAccess
fields = [
"id",
"user",
"role",
"created_at",
"updated_at",
"can_set_role_to",
]
read_only_fields = ["id", "user", "can_set_role_to"]

def get_can_set_role_to(self, access):
"""Return roles available to set"""
roles = list(enums.MailDomainRoleChoices)
# get role of authenticated user
authenticated_user_role = access.user_role
if authenticated_user_role != enums.MailDomainRoleChoices.OWNER:
roles.remove(enums.MailDomainRoleChoices.OWNER)
# if the user authenticated is a viewer, they can't modify role
# and only an owner can change role of an owner
if authenticated_user_role == enums.MailDomainRoleChoices.VIEWER or (
authenticated_user_role != enums.MailDomainRoleChoices.OWNER
and access.role == enums.MailDomainRoleChoices.OWNER
):
return []
# we only want to return other roles available to change,
# so we remove the current role of current access.
roles.remove(access.role)
return sorted(roles)


class MailDomainAccessReadOnlySerializer(MailDomainAccessSerializer):
"""Serialize mail domain access for list and retrieve actions."""

class Meta:
model = models.MailDomainAccess
fields = [
"id",
"user",
"role",
"can_set_role_to",
]
read_only_fields = [
"id",
"user",
"role",
"can_set_role_to",
]
read_only_fields = ["id"]
58 changes: 54 additions & 4 deletions src/backend/mailbox_manager/api/viewsets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""API endpoints"""

from django.db.models import Subquery

from rest_framework import filters, mixins, viewsets
from rest_framework import permissions as drf_permissions

Expand Down Expand Up @@ -54,19 +56,67 @@ def perform_create(self, serializer):

# pylint: disable=too-many-ancestors
class MailDomainAccessViewSet(
mixins.ListModelMixin,
viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
):
"""
MailDomainAccess viewset.
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.
"""

permission_classes = [drf_permissions.IsAuthenticated]
serializer_class = serializers.MailDomainAccessSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ["created_at", "user", "domain", "role"]
ordering_fields = ["role", "user__email", "user__name"]
ordering = ["-created_at"]
queryset = models.MailDomainAccess.objects.all()
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):
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"]
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


class MailBoxViewSet(
Expand Down
Loading

0 comments on commit dd8bd2a

Please sign in to comment.