✨(mailbox) allow to disable mailbox
We send a request to dimail API and change mailbox status to disabled. A disabled mailbox can no longer be used thus access to webmail is disabled for user.
This commit is contained in:
@@ -10,6 +10,7 @@ and this project adheres to
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- ✨(mailbox) allow to disable mailbox
|
||||||
- ✨(backend) add ServiceProvider #522
|
- ✨(backend) add ServiceProvider #522
|
||||||
- 💄(admin) allow header color customization #552
|
- 💄(admin) allow header color customization #552
|
||||||
|
|
||||||
|
|||||||
@@ -519,8 +519,8 @@ msgid "Mailboxes"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: mailbox_manager/models.py:224
|
#: mailbox_manager/models.py:224
|
||||||
msgid "You can't create a mailbox for a disabled domain."
|
msgid "You can't create or update a mailbox for a disabled domain."
|
||||||
msgstr "Vous ne pouvez pas créer de boîte mail pour un domain désactivé."
|
msgstr "Vous ne pouvez pas créer ou modifier une boîte mail pour un domain désactivé."
|
||||||
|
|
||||||
#: mailbox_manager/utils/dimail.py:183
|
#: mailbox_manager/utils/dimail.py:183
|
||||||
msgid "Your new mailbox information"
|
msgid "Your new mailbox information"
|
||||||
|
|||||||
@@ -3,12 +3,15 @@
|
|||||||
from django.db.models import Subquery
|
from django.db.models import Subquery
|
||||||
|
|
||||||
from rest_framework import exceptions, filters, mixins, viewsets
|
from rest_framework import exceptions, filters, mixins, viewsets
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from core import models as core_models
|
from core import models as core_models
|
||||||
|
|
||||||
from mailbox_manager import enums, models
|
from mailbox_manager import enums, models
|
||||||
from mailbox_manager.api import permissions
|
from mailbox_manager.api import permissions
|
||||||
from mailbox_manager.api.client import serializers
|
from mailbox_manager.api.client import serializers
|
||||||
|
from mailbox_manager.utils.dimail import DimailAPIClient
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-ancestors
|
# pylint: disable=too-many-ancestors
|
||||||
@@ -186,15 +189,18 @@ class MailBoxViewSet(
|
|||||||
):
|
):
|
||||||
"""MailBox ViewSet
|
"""MailBox ViewSet
|
||||||
|
|
||||||
GET /api/<version>/mail-domains/<domain-slug>/mailboxes/
|
GET /api/<version>/mail-domains/<domain_slug>/mailboxes/
|
||||||
Return a list of mailboxes on the domain
|
Return a list of mailboxes on the domain
|
||||||
|
|
||||||
POST /api/<version>/mail-domains/<domain-slug>/mailboxes/ with expected data:
|
POST /api/<version>/mail-domains/<domain_slug>/mailboxes/ with expected data:
|
||||||
- first_name: str
|
- first_name: str
|
||||||
- last_name: str
|
- last_name: str
|
||||||
- local_part: str
|
- local_part: str
|
||||||
- secondary_email: str
|
- secondary_email: str
|
||||||
Sends request to email provisioning API and returns newly created mailbox
|
Sends request to email provisioning API and returns newly created mailbox
|
||||||
|
|
||||||
|
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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_classes = [permissions.MailBoxPermission]
|
permission_classes = [permissions.MailBoxPermission]
|
||||||
@@ -218,3 +224,13 @@ class MailBoxViewSet(
|
|||||||
slug=domain_slug
|
slug=domain_slug
|
||||||
)
|
)
|
||||||
super().perform_create(serializer)
|
super().perform_create(serializer)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|||||||
@@ -215,17 +215,12 @@ class Mailbox(BaseModel):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Modification is forbidden for now.
|
Override save function to not allow to create or update mailbox of a disabled domain.
|
||||||
"""
|
"""
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
|
|
||||||
if self.domain.status == MailDomainStatusChoices.DISABLED:
|
if self.domain.status == MailDomainStatusChoices.DISABLED:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_("You can't create a mailbox for a disabled domain.")
|
_("You can't create or update a mailbox for a disabled domain.")
|
||||||
)
|
)
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
if self._state.adding:
|
|
||||||
return super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
# Update is not implemented for now
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|||||||
@@ -264,7 +264,9 @@ def test_api_mailboxes__cannot_create_on_disabled_domain(role):
|
|||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||||
assert not models.Mailbox.objects.exists()
|
assert not models.Mailbox.objects.exists()
|
||||||
assert response.json() == ["You can't create a mailbox for a disabled domain."]
|
assert response.json() == [
|
||||||
|
"You can't create or update a mailbox for a disabled domain."
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for the mailbox API
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import responses
|
||||||
|
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_mailboxes__disable_anonymous_forbidden():
|
||||||
|
"""Anonymous users should not be able to disable a mailbox via the API."""
|
||||||
|
mailbox = factories.MailboxEnabledFactory()
|
||||||
|
response = APIClient().post(
|
||||||
|
f"/api/v1.0/mail-domains/{mailbox.domain.slug}/mailboxes/{mailbox.pk}/disable/",
|
||||||
|
)
|
||||||
|
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||||
|
assert models.Mailbox.objects.get().status == enums.MailboxStatusChoices.ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mailboxes__disable_authenticated_failure():
|
||||||
|
"""Authenticated users should not be able to disable mailbox
|
||||||
|
without specific role on mail domain."""
|
||||||
|
user = core_factories.UserFactory()
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
mailbox = factories.MailboxEnabledFactory()
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/mail-domains/{mailbox.domain.slug}/mailboxes/{mailbox.pk}/disable/",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
assert models.Mailbox.objects.get().status == enums.MailboxStatusChoices.ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mailboxes__disable_viewer_failure():
|
||||||
|
"""Users with viewer role should not be able to disable mailbox on the mail domain."""
|
||||||
|
mailbox = factories.MailboxEnabledFactory()
|
||||||
|
access = factories.MailDomainAccessFactory(
|
||||||
|
role=enums.MailDomainRoleChoices.VIEWER, domain=mailbox.domain
|
||||||
|
)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(access.user)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/mail-domains/{mailbox.domain.slug}/mailboxes/{mailbox.pk}/disable/",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
assert models.Mailbox.objects.get().status == enums.MailboxStatusChoices.ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"role",
|
||||||
|
[enums.MailDomainRoleChoices.OWNER, enums.MailDomainRoleChoices.ADMIN],
|
||||||
|
)
|
||||||
|
def test_api_mailboxes__disable_roles_success(role):
|
||||||
|
"""Users with owner or admin role should be able to disable mailbox on the mail domain."""
|
||||||
|
mailbox = factories.MailboxEnabledFactory()
|
||||||
|
access = factories.MailDomainAccessFactory(role=role, domain=mailbox.domain)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(access.user)
|
||||||
|
|
||||||
|
with responses.RequestsMock() as rsps:
|
||||||
|
# Ensure successful response using "responses":
|
||||||
|
rsps.add(
|
||||||
|
rsps.GET,
|
||||||
|
re.compile(r".*/token/"),
|
||||||
|
body='{"access_token": "domain_owner_token"}',
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
rsps.add(
|
||||||
|
rsps.PATCH,
|
||||||
|
re.compile(
|
||||||
|
rf".*/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}"
|
||||||
|
),
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/mail-domains/{mailbox.domain.slug}/mailboxes/{mailbox.pk}/disable/",
|
||||||
|
)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
mailbox = models.Mailbox.objects.get()
|
||||||
|
|
||||||
|
assert mailbox.status == enums.MailboxStatusChoices.DISABLED
|
||||||
@@ -116,7 +116,7 @@ def test_models_mailboxes__cannot_create_mailboxes_on_disabled_domain():
|
|||||||
A disabled status for the mail domain raises an error."""
|
A disabled status for the mail domain raises an error."""
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
exceptions.ValidationError,
|
exceptions.ValidationError,
|
||||||
match="You can't create a mailbox for a disabled domain.",
|
match="You can't create or update a mailbox for a disabled domain.",
|
||||||
):
|
):
|
||||||
factories.MailboxFactory(
|
factories.MailboxFactory(
|
||||||
domain=factories.MailDomainFactory(
|
domain=factories.MailDomainFactory(
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class DimailAPIClient:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def import_mailboxes(self, domain):
|
def import_mailboxes(self, domain):
|
||||||
"""Synchronize mailboxes from dimail - open xchange to our database.
|
"""Import mailboxes from dimail - open xchange in our database.
|
||||||
This is useful in case of acquisition of a pre-existing mail domain.
|
This is useful in case of acquisition of a pre-existing mail domain.
|
||||||
Mailboxes created here are not new mailboxes and will not trigger mail notification."""
|
Mailboxes created here are not new mailboxes and will not trigger mail notification."""
|
||||||
|
|
||||||
@@ -272,3 +272,22 @@ class DimailAPIClient:
|
|||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
return imported_mailboxes
|
return imported_mailboxes
|
||||||
|
|
||||||
|
def disable_mailbox(self, mailbox, user_sub=None):
|
||||||
|
"""Send a request to disable a mailbox to dimail API"""
|
||||||
|
response = session.patch(
|
||||||
|
f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}",
|
||||||
|
json={"active": "no"},
|
||||||
|
headers=self.get_headers(user_sub),
|
||||||
|
verify=True,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
if response.status_code == status.HTTP_200_OK:
|
||||||
|
logger.info(
|
||||||
|
"Mailbox %s successfully desactivated on domain %s by user %s",
|
||||||
|
str(mailbox),
|
||||||
|
str(mailbox.domain),
|
||||||
|
user_sub,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
return self.raise_exception_for_unexpected_response(response)
|
||||||
|
|||||||
Reference in New Issue
Block a user