(dimail) send domain creation request to dimail

Send domain creation request to dimail when someone creates a domain in people.
This commit is contained in:
Marie PUPO JEAMMET
2024-10-23 18:44:14 +02:00
committed by Marie
parent 8f30264445
commit 21bf431940
5 changed files with 166 additions and 8 deletions

View File

@@ -18,6 +18,7 @@ and this project adheres to
### Added
- ✨(dimail) send domain creation requests to dimail #454
- ✨(dimail) synchronize mailboxes from dimail to our db #453
- ✨(ci) add security scan #429
- ✨(teams) register contacts on admin views

View File

@@ -15,6 +15,8 @@ As dimail's primary goal is to act as an interface between People and OX, its ar
Upon creating a domain on People, the same domain is created on dimail and will undergo a series of checks. When all checks have passed, the domain is considered valid and mailboxes can be created on it.
Domains in OX have a field called "context". Context is a shared space between domains, allowing users to discover users not only on their domain but on their entire context.
### Users
The ones issuing requests. Dimail users will reflect domains owners and administrators on People, for logging purposes and to allow direct use of dimail, if desired. User reconciliation is made on user uuid provided by ProConnect.

View File

@@ -77,6 +77,19 @@ class MailDomainSerializer(serializers.ModelSerializer):
return domain.get_abilities(request.user)
return {}
def create(self, validated_data):
"""
Override create function to fire a request to dimail upon domain creation.
"""
# send new domain request to dimail
client = DimailAPIClient()
client.send_domain_creation_request(
validated_data["name"], self.context["request"].user.sub
)
# no exception raised ? Then actually save domain on our database
return models.MailDomain.objects.create(**validated_data)
class MailDomainAccessSerializer(serializers.ModelSerializer):
"""Serialize mail domain access."""

View File

@@ -2,7 +2,13 @@
Tests for MailDomains API endpoint in People's app mailbox_manager. Focus on "create" action.
"""
import re
from logging import Logger
from unittest import mock
import pytest
import responses
from requests.exceptions import HTTPError
from rest_framework import status
from rest_framework.test import APIClient
@@ -56,13 +62,26 @@ def test_api_mail_domains__create_authenticated():
client = APIClient()
client.force_login(user)
response = client.post(
"/api/v1.0/mail-domains/",
{
"name": "mydomain.com",
},
format="json",
)
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",
)
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()
@@ -79,5 +98,94 @@ def test_api_mail_domains__create_authenticated():
# a new domain with status "pending" is created and authenticated user is the owner
assert domain.status == enums.MailDomainStatusChoices.PENDING
assert domain.name == "mydomain.com"
assert domain.name == domain_name
assert domain.accesses.filter(role="owner", user=user).exists()
## SYNC TO DIMAIL
@mock.patch.object(Logger, "info")
def test_api_mail_domains__create_dimail_domain(mock_info):
"""
Creating a domain should trigger a call to create a domain on dimail too.
"""
user = core_factories.UserFactory()
client = APIClient()
client.force_login(user)
domain_name = "test.fr"
with responses.RequestsMock() as rsps:
rsp = rsps.add(
rsps.POST,
re.compile(r".*/domains/"),
body=str(
{
"name": domain_name,
}
),
status=status.HTTP_201_CREATED,
content_type="application/json",
)
response = client.post(
"/api/v1.0/mail-domains/",
{
"name": domain_name,
},
format="json",
)
assert response.status_code == status.HTTP_201_CREATED
assert rsp.call_count == 1 # endpoint was called
# Logger
assert (
mock_info.call_count == 2
) # should be 1. A new empty info has been added. To be investigated
assert mock_info.call_args_list[0][0] == (
"Domain %s successfully created on dimail by user %s",
domain_name,
user.sub,
)
def test_api_mail_domains__no_creation_when_dimail_duplicate(caplog):
"""No domain should be created when dimail returns a 409 conflict."""
user = core_factories.UserFactory()
client = APIClient()
client.force_login(user)
domain_name = "test.fr"
dimail_error = {
"status_code": status.HTTP_409_CONFLICT,
"detail": "Domain already exists",
}
with responses.RequestsMock() as rsps:
rsp = rsps.add(
rsps.POST,
re.compile(r".*/domains/"),
body=str({"detail": dimail_error["detail"]}),
status=dimail_error["status_code"],
content_type="application/json",
)
with pytest.raises(HTTPError):
response = client.post(
"/api/v1.0/mail-domains/",
{
"name": domain_name,
},
format="json",
)
assert rsp.call_count == 1 # endpoint was called
assert response.status_code == dimail_error["status_code"]
assert response.json() == {"detail": dimail_error["detail"]}
# check logs
assert len(caplog.records) == 2 # 1 + new empty info. To be investigated
record = caplog.records[0]
assert record.levelname == "ERROR"
assert (
record.message
== f"[DIMAIL] {dimail_error['status_code']}: {dimail_error['detail']}"
)

View File

@@ -76,6 +76,40 @@ class DimailAPIClient:
return self.pass_dimail_unexpected_response(response)
def send_domain_creation_request(self, domain_name, request_user):
"""Send a domain creation request to dimail API."""
payload = {
"domain": domain_name,
"context": domain_name, # for now, we put each domain on its own context
"features": ["webmail", "mailboxes"],
}
try:
response = session.post(
f"{self.API_URL}/domains/",
json=payload,
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
verify=True,
timeout=10,
)
except requests.exceptions.ConnectionError as error:
logger.error(
"Connection error while trying to reach %s.",
self.API_URL,
exc_info=error,
)
raise error
if response.status_code == status.HTTP_201_CREATED:
logger.info(
"Domain %s successfully created on dimail by user %s",
domain_name,
request_user,
)
return response
return self.pass_dimail_unexpected_response(response)
def send_mailbox_request(self, mailbox, user_sub=None):
"""Send a CREATE mailbox request to mail provisioning API."""