✨(backend) domain accesses create API
Allow to create (POST) a new access for a domain. Role can be change only to a role available and depending to the authenticated user.
This commit is contained in:
@@ -19,6 +19,7 @@ and this project adheres to
|
||||
- ✨(frontend) allow group members filtering #363
|
||||
- ✨(mailbox) send new mailbox confirmation email #397
|
||||
- ✨(domains) domain accesses update API #423
|
||||
- ✨(backend) domain accesses create API #428
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ import json
|
||||
from rest_framework import exceptions, serializers
|
||||
|
||||
from core.api.serializers import UserSerializer
|
||||
from core.models import User
|
||||
|
||||
from mailbox_manager import models
|
||||
from mailbox_manager import enums, models
|
||||
from mailbox_manager.utils.dimail import DimailAPIClient
|
||||
|
||||
|
||||
@@ -85,13 +86,13 @@ class MailDomainAccessSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = models.MailDomainAccess
|
||||
fields = [
|
||||
"id",
|
||||
"user",
|
||||
"role",
|
||||
"can_set_role_to",
|
||||
]
|
||||
read_only_fields = ["id", "user", "can_set_role_to"]
|
||||
fields = ["id", "user", "role", "can_set_role_to"]
|
||||
read_only_fields = ["id", "can_set_role_to"]
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""Make "user" field is readonly but only on update."""
|
||||
validated_data.pop("user", None)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
def get_can_set_role_to(self, access):
|
||||
"""Return roles available to set for the authenticated user"""
|
||||
@@ -99,8 +100,9 @@ class MailDomainAccessSerializer(serializers.ModelSerializer):
|
||||
|
||||
def validate(self, attrs):
|
||||
"""
|
||||
Check access rights specific to writing (update)
|
||||
Check access rights specific to writing (update/create)
|
||||
"""
|
||||
|
||||
request = self.context.get("request")
|
||||
authenticated_user = getattr(request, "user", None)
|
||||
role = attrs.get("role")
|
||||
@@ -116,6 +118,45 @@ class MailDomainAccessSerializer(serializers.ModelSerializer):
|
||||
else "You are not allowed to modify role for this user."
|
||||
)
|
||||
raise exceptions.PermissionDenied(message)
|
||||
# Create
|
||||
else:
|
||||
# A domain slug has to be set to create a new access
|
||||
try:
|
||||
domain_slug = self.context["domain_slug"]
|
||||
except KeyError as exc:
|
||||
raise exceptions.ValidationError(
|
||||
"You must set a domain slug in kwargs to create a new domain access."
|
||||
) from exc
|
||||
|
||||
try:
|
||||
access = authenticated_user.mail_domain_accesses.get(
|
||||
domain__slug=domain_slug
|
||||
)
|
||||
except models.MailDomainAccess.DoesNotExist as exc:
|
||||
raise exceptions.PermissionDenied(
|
||||
"You are not allowed to manage accesses for this domain."
|
||||
) from exc
|
||||
|
||||
# Authenticated user must be owner or admin of current domain to set new roles
|
||||
if access.role not in [
|
||||
enums.MailDomainRoleChoices.OWNER,
|
||||
enums.MailDomainRoleChoices.ADMIN,
|
||||
]:
|
||||
raise exceptions.PermissionDenied(
|
||||
"You are not allowed to manage accesses for this domain."
|
||||
)
|
||||
# only an owner can set an owner role to another user
|
||||
if (
|
||||
role == enums.MailDomainRoleChoices.OWNER
|
||||
and access.role != enums.MailDomainRoleChoices.OWNER
|
||||
):
|
||||
raise exceptions.PermissionDenied(
|
||||
"Only owners of a domain can assign other users as owners."
|
||||
)
|
||||
attrs["user"] = User.objects.get(pk=self.initial_data["user"])
|
||||
attrs["domain"] = models.MailDomain.objects.get(
|
||||
slug=self.context["domain_slug"]
|
||||
)
|
||||
return attrs
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ class MailDomainViewSet(
|
||||
class MailDomainAccessViewSet(
|
||||
viewsets.GenericViewSet,
|
||||
mixins.ListModelMixin,
|
||||
mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
):
|
||||
@@ -68,6 +69,11 @@ class MailDomainAccessViewSet(
|
||||
Return list of all domain accesses related to the logged-in user and one
|
||||
domain access if an id is provided.
|
||||
|
||||
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
|
||||
|
||||
PUT /api/v1.0/mail-domains/<domain_slug>/accesses/<domain_access_id>/ with expected data:
|
||||
- role: str [owner|admin|viewer]
|
||||
Return updated domain access
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Test for mail domain accesses API endpoints in People's core app : create
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
import pytest
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories as core_factories
|
||||
|
||||
from mailbox_manager import enums, factories, models
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_mail_domain__accesses_create_anonymous():
|
||||
"""Anonymous users should not be allowed to create mail domain accesses."""
|
||||
user = core_factories.UserFactory()
|
||||
mail_domain = factories.MailDomainFactory()
|
||||
for role in [role[0] for role in enums.MailDomainRoleChoices.choices]:
|
||||
response = APIClient().post(
|
||||
f"/api/v1.0/mail-domains/{mail_domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
assert models.MailDomainAccess.objects.exists() is False
|
||||
|
||||
|
||||
def test_api_mail_domain__accesses_create_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to create domain accesses for a domain to
|
||||
which they are not related.
|
||||
"""
|
||||
user = core_factories.UserFactory()
|
||||
other_user = core_factories.UserFactory()
|
||||
domain = factories.MailDomainFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
for role in [role[0] for role in enums.MailDomainRoleChoices.choices]:
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert response.json() == {
|
||||
"detail": "You are not allowed to manage accesses for this domain."
|
||||
}
|
||||
assert not models.MailDomainAccess.objects.filter(user=other_user).exists()
|
||||
|
||||
|
||||
def test_api_mail_domain__accesses_create_authenticated_viewer():
|
||||
"""Viewer of a mail domain should not be allowed to create mail domain accesses."""
|
||||
authenticated_user = core_factories.UserFactory()
|
||||
mail_domain = factories.MailDomainFactory(
|
||||
users=[(authenticated_user, enums.MailDomainRoleChoices.VIEWER)]
|
||||
)
|
||||
other_user = core_factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(authenticated_user)
|
||||
for role in [role[0] for role in enums.MailDomainRoleChoices.choices]:
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{mail_domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert response.json() == {
|
||||
"detail": "You are not allowed to manage accesses for this domain."
|
||||
}
|
||||
|
||||
assert not models.MailDomainAccess.objects.filter(user=other_user).exists()
|
||||
|
||||
|
||||
def test_api_mail_domain__accesses_create_authenticated_administrator():
|
||||
"""
|
||||
Administrators of a domain should be able to create mail domain accesses
|
||||
except for the "owner" role.
|
||||
"""
|
||||
authenticated_user = core_factories.UserFactory()
|
||||
mail_domain = factories.MailDomainFactory(
|
||||
users=[(authenticated_user, enums.MailDomainRoleChoices.ADMIN)]
|
||||
)
|
||||
other_user = core_factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(authenticated_user)
|
||||
|
||||
# It should not be allowed to create an owner access
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{mail_domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": enums.MailDomainRoleChoices.OWNER,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert response.json() == {
|
||||
"detail": "Only owners of a domain can assign other users as owners."
|
||||
}
|
||||
|
||||
# It should be allowed to create a lower access
|
||||
for role in [enums.MailDomainRoleChoices.ADMIN, enums.MailDomainRoleChoices.VIEWER]:
|
||||
other_user = core_factories.UserFactory()
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{mail_domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
new_mail_domain_access = models.MailDomainAccess.objects.filter(
|
||||
user=other_user
|
||||
).last()
|
||||
|
||||
assert response.json()["id"] == str(new_mail_domain_access.id)
|
||||
assert response.json()["role"] == role
|
||||
assert models.MailDomainAccess.objects.filter(domain=mail_domain).count() == 3
|
||||
|
||||
|
||||
def test_api_mail_domain__accesses_create_authenticated_owner():
|
||||
"""
|
||||
Owners of a mail domain should be able to create mail domain accesses whatever the role.
|
||||
"""
|
||||
authenticated_user = core_factories.UserFactory()
|
||||
mail_domain = factories.MailDomainFactory(
|
||||
users=[(authenticated_user, enums.MailDomainRoleChoices.OWNER)]
|
||||
)
|
||||
other_user = core_factories.UserFactory()
|
||||
|
||||
role = random.choice([role[0] for role in enums.MailDomainRoleChoices.choices])
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(authenticated_user)
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{mail_domain.slug}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert models.MailDomainAccess.objects.filter(user=other_user).count() == 1
|
||||
new_mail_domain_access = models.MailDomainAccess.objects.filter(
|
||||
user=other_user
|
||||
).get()
|
||||
assert response.json()["id"] == str(new_mail_domain_access.id)
|
||||
assert response.json()["role"] == role
|
||||
Reference in New Issue
Block a user