(dimail) check domain health

Call dimail to check if a domain still works.
Turn domain into failure status if dimail returns broken state.
And enable domain if dimail returns ok state.
This commit is contained in:
Sabrina Demagny
2024-12-27 20:07:18 +01:00
parent 5d2e63fc18
commit 0abfd49fee
4 changed files with 134 additions and 3 deletions

View File

@@ -27,6 +27,7 @@ and this project adheres to
### Added
- ✨(dimail) check domain health
- ✨(frontend) disable mailbox and allow to create pending mailbox
- ✨(organizations) add siret to name conversion #584
- 💄(frontend) redirect home according to abilities #588

View File

@@ -0,0 +1,82 @@
# pylint: disable=line-too-long
"""Define here some fake data from dimail, useful to mock dimail response"""
CHECK_DOMAIN_BROKEN = {
"name": "example.fr",
"state": "broken",
"valid": False,
"delivery": "virtual",
"features": ["webmail", "mailbox"],
"webmail_domain": None,
"imap_domain": None,
"smtp_domain": None,
"context_name": "example.fr",
"transport": None,
"domain_exist": {"ok": True, "internal": False, "errors": []},
"mx": {
"ok": False,
"internal": False,
"errors": [
{
"code": "wrong_mx",
"detail": "Je veux que le MX du domaine soit mx.ox.numerique.gouv.fr., or je trouve example-fr.mail.protection.outlook.com.",
}
],
},
"cname_imap": {
"ok": False,
"internal": False,
"errors": [
{
"code": "no_cname_imap",
"detail": "Il faut un CNAME 'imap.example.fr' qui renvoie vers 'imap.ox.numerique.gouv.fr.'",
}
],
},
"cname_smtp": {
"ok": False,
"internal": False,
"errors": [
{
"code": "wrong_cname_smtp",
"detail": "Le CNAME pour 'smtp.example.fr' n'est pas bon, il renvoie vers 'ns0.ovh.net.' et je veux 'smtp.ox.numerique.gouv.fr.'",
}
],
},
"cname_webmail": {
"ok": False,
"internal": False,
"errors": [
{
"code": "no_cname_webmail",
"detail": "Il faut un CNAME 'webmail.example.fr' qui renvoie vers 'webmail.ox.numerique.gouv.fr.'",
}
],
},
"spf": {
"ok": False,
"internal": False,
"errors": [
{
"code": "wrong_spf",
"detail": "Le SPF record ne contient pas include:_spf.ox.numerique.gouv.fr",
}
],
},
"dkim": {
"ok": False,
"internal": False,
"errors": [
{"code": "no_dkim", "detail": "Il faut un DKIM record, avec la bonne clef"}
],
},
"postfix": {"ok": True, "internal": True, "errors": []},
"ox": {"ok": True, "internal": True, "errors": []},
"cert": {
"ok": False,
"internal": True,
"errors": [
{"code": "no_cert", "detail": "Pas de certificat pour ce domaine (ls)"}
],
},
}

View File

@@ -2,6 +2,7 @@
Unit tests for dimail client
"""
import json
import re
from email.errors import HeaderParseError, NonASCIILocalPartDefect
from logging import Logger
@@ -11,9 +12,11 @@ import pytest
import responses
from rest_framework import status
from mailbox_manager import factories, models
from mailbox_manager import enums, factories, models
from mailbox_manager.utils.dimail import DimailAPIClient
from .fixtures.dimail import CHECK_DOMAIN_BROKEN
pytestmark = pytest.mark.django_db
@@ -154,3 +157,22 @@ def test_dimail_synchronization__synchronize_mailboxes(mock_warning):
mailbox = models.Mailbox.objects.get()
assert mailbox.local_part == "oxadmin"
assert imported_mailboxes == [mailbox_valid["email"]]
def test_dimail__fetch_domain_status_from_dimail():
"""Request to dimail health status of a domain"""
domain = factories.MailDomainEnabledFactory()
with responses.RequestsMock() as rsps:
body_content_domain = CHECK_DOMAIN_BROKEN.copy()
body_content_domain["name"] = domain.name
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain.name}/check/"),
body=json.dumps(body_content_domain),
status=status.HTTP_200_OK,
content_type="application/json",
)
dimail_client = DimailAPIClient()
response = dimail_client.fetch_domain_status(domain)
assert response.status_code == status.HTTP_200_OK
assert domain.status == enums.MailDomainStatusChoices.FAILED

View File

@@ -17,7 +17,7 @@ import requests
from rest_framework import status
from urllib3.util import Retry
from mailbox_manager import models
from mailbox_manager import enums, models
logger = getLogger(__name__)
@@ -249,7 +249,8 @@ class DimailAPIClient:
"[DIMAIL] unexpected error : %s %s", response.status_code, error_content
)
raise requests.exceptions.HTTPError(
f"Unexpected response from dimail: {response.status_code} {error_content}"
f"Unexpected response from dimail: {response.status_code} "
f"{error_content.get('detail') or error_content}"
)
def notify_mailbox_creation(self, recipient, mailbox_data):
@@ -394,3 +395,28 @@ class DimailAPIClient:
)
return response
return self.raise_exception_for_unexpected_response(response)
def fetch_domain_status(self, domain):
"""Send a request to check domain and update status of our domain."""
response = session.get(
f"{self.API_URL}/domains/{domain.name}/check/",
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
verify=True,
timeout=10,
)
if response.status_code == status.HTTP_200_OK:
dimail_status = response.json()["state"]
if (
domain.status != enums.MailDomainStatusChoices.ENABLED
and dimail_status == "ok"
):
domain.status = enums.MailDomainStatusChoices.ENABLED
domain.save()
elif (
domain.status != enums.MailDomainStatusChoices.FAILED
and dimail_status == "broken"
):
domain.status = enums.MailDomainStatusChoices.FAILED
domain.save()
return response
return self.raise_exception_for_unexpected_response(response)