✨(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:
@@ -27,6 +27,7 @@ and this project adheres to
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- ✨(dimail) check domain health
|
||||||
- ✨(frontend) disable mailbox and allow to create pending mailbox
|
- ✨(frontend) disable mailbox and allow to create pending mailbox
|
||||||
- ✨(organizations) add siret to name conversion #584
|
- ✨(organizations) add siret to name conversion #584
|
||||||
- 💄(frontend) redirect home according to abilities #588
|
- 💄(frontend) redirect home according to abilities #588
|
||||||
|
|||||||
82
src/backend/mailbox_manager/tests/fixtures/dimail.py
vendored
Normal file
82
src/backend/mailbox_manager/tests/fixtures/dimail.py
vendored
Normal 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)"}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
Unit tests for dimail client
|
Unit tests for dimail client
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
from email.errors import HeaderParseError, NonASCIILocalPartDefect
|
from email.errors import HeaderParseError, NonASCIILocalPartDefect
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
@@ -11,9 +12,11 @@ import pytest
|
|||||||
import responses
|
import responses
|
||||||
from rest_framework import status
|
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 mailbox_manager.utils.dimail import DimailAPIClient
|
||||||
|
|
||||||
|
from .fixtures.dimail import CHECK_DOMAIN_BROKEN
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
@@ -154,3 +157,22 @@ def test_dimail_synchronization__synchronize_mailboxes(mock_warning):
|
|||||||
mailbox = models.Mailbox.objects.get()
|
mailbox = models.Mailbox.objects.get()
|
||||||
assert mailbox.local_part == "oxadmin"
|
assert mailbox.local_part == "oxadmin"
|
||||||
assert imported_mailboxes == [mailbox_valid["email"]]
|
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
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import requests
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from urllib3.util import Retry
|
from urllib3.util import Retry
|
||||||
|
|
||||||
from mailbox_manager import models
|
from mailbox_manager import enums, models
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -249,7 +249,8 @@ class DimailAPIClient:
|
|||||||
"[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(
|
||||||
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):
|
def notify_mailbox_creation(self, recipient, mailbox_data):
|
||||||
@@ -394,3 +395,28 @@ class DimailAPIClient:
|
|||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
return self.raise_exception_for_unexpected_response(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)
|
||||||
|
|||||||
Reference in New Issue
Block a user