🐛(maildomain) fix domain access creation

During a new domain creation, a call to dimail is made
to create user/allow on dimail side before owner role creation
on our side.
So when user/allow creation on dimain side fails,
the owner role is not created on our side.
Therefore the domain is created but invisible on the user interface.
The user will probably try to create the same domain again
and see the error message 'this domain already exists'.
To avoid this we make sure to create owner role on our side
despite dimail failure and set domain to failed status to retry
later dimail access creation.
This commit is contained in:
Sabrina Demagny
2024-12-16 22:28:42 +01:00
parent fa80edfaa8
commit a021e9eec6
4 changed files with 90 additions and 5 deletions

View File

@@ -8,6 +8,10 @@ and this project adheres to
## [Unreleased] ## [Unreleased]
### Fixed
- 🐛(backend) fix manage roles on domain admin view
### Added ### Added
- ✨(organizations) add siret to name conversion #584 - ✨(organizations) add siret to name conversion #584

View File

@@ -1,7 +1,9 @@
"""Client serializers for People's mailbox manager app.""" """Client serializers for People's mailbox manager app."""
import json import json
from logging import getLogger
from requests.exceptions import HTTPError
from rest_framework import exceptions, serializers from rest_framework import exceptions, serializers
from core.api.client.serializers import UserSerializer from core.api.client.serializers import UserSerializer
@@ -10,6 +12,8 @@ from core.models import User
from mailbox_manager import enums, models from mailbox_manager import enums, models
from mailbox_manager.utils.dimail import DimailAPIClient from mailbox_manager.utils.dimail import DimailAPIClient
logger = getLogger(__name__)
class MailboxSerializer(serializers.ModelSerializer): class MailboxSerializer(serializers.ModelSerializer):
"""Serialize mailbox.""" """Serialize mailbox."""
@@ -190,14 +194,20 @@ class MailDomainAccessSerializer(serializers.ModelSerializer):
""" """
dimail = DimailAPIClient() dimail = DimailAPIClient()
user = validated_data["user"]
domain = validated_data["domain"]
if validated_data["role"] in [ if validated_data["role"] in [
enums.MailDomainRoleChoices.ADMIN, enums.MailDomainRoleChoices.ADMIN,
enums.MailDomainRoleChoices.OWNER, enums.MailDomainRoleChoices.OWNER,
]: ]:
dimail.create_user(validated_data["user"].sub) try:
dimail.create_allow( dimail.create_user(user.sub)
validated_data["user"].sub, validated_data["domain"].name dimail.create_allow(user.sub, domain.name)
) except HTTPError:
logger.exception("[DIMAIL] access creation failed %s")
domain.status = enums.MailDomainStatusChoices.FAILED
domain.save()
return super().create(validated_data) return super().create(validated_data)

View File

@@ -122,6 +122,77 @@ def test_api_mail_domains__create_authenticated():
assert domain.accesses.filter(role="owner", user=user).exists() assert domain.accesses.filter(role="owner", user=user).exists()
def test_api_mail_domains__create_authenticated__dimail_failure():
"""
Despite a dimail failure for user and/or allow creation,
an authenticated user should be able to create a mail domain
and should automatically be added as owner of the newly created domain.
"""
user = core_factories.UserFactory()
client = APIClient()
client.force_login(user)
domain_name = "test.domain.fr"
with responses.RequestsMock() as rsps:
rsps.add(
rsps.POST,
re.compile(r".*/domains/"),
body=str(
{
"name": domain_name,
}
),
status=status.HTTP_201_CREATED,
content_type="application/json",
)
rsps.add(
rsps.POST,
re.compile(r".*/users/"),
body=str(
{
"name": "request-user-sub",
"is_admin": "false",
"uuid": "user-uuid-on-dimail",
"perms": [],
}
),
status=status.HTTP_201_CREATED,
content_type="application/json",
)
rsps.add(
rsps.POST,
re.compile(r".*/allows/"),
body=str({"user": "request-user-sub", "domain": str(domain_name)}),
status=status.HTTP_403_FORBIDDEN,
content_type="application/json",
)
response = client.post(
"/api/v1.0/mail-domains/",
{"name": domain_name, "context": "null", "features": ["webmail"]},
format="json",
)
assert response.status_code == status.HTTP_201_CREATED
domain = models.MailDomain.objects.get()
# response is as expected
assert response.json() == {
"id": str(domain.id),
"name": domain.name,
"slug": domain.slug,
"status": enums.MailDomainStatusChoices.FAILED,
"created_at": domain.created_at.isoformat().replace("+00:00", "Z"),
"updated_at": domain.updated_at.isoformat().replace("+00:00", "Z"),
"abilities": domain.get_abilities(user),
}
# a new domain with status "failed" is created and authenticated user is the owner
assert domain.status == enums.MailDomainStatusChoices.FAILED
assert domain.name == domain_name
assert domain.accesses.filter(role="owner", user=user).exists()
## SYNC TO DIMAIL ## SYNC TO DIMAIL
def test_api_mail_domains__create_dimail_domain(caplog): def test_api_mail_domains__create_dimail_domain(caplog):
""" """

View File

@@ -245,7 +245,7 @@ class DimailAPIClient:
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
error_content = response.content.decode(response.encoding) error_content = response.content.decode(response.encoding)
logger.error( logger.exception(
"[DIMAIL] unexpected error : %s %s", response.status_code, error_content "[DIMAIL] unexpected error : %s %s", response.status_code, error_content
) )
raise requests.exceptions.HTTPError( raise requests.exceptions.HTTPError(