🐛(dimail) ignore oxadmin mailboxes when importing mailboxes

oxadmin mailbox are technical mailboxes used by dimail. It should
not be imported when importing mailboxes from dimail.
This commit is contained in:
Marie PUPO JEAMMET
2026-01-12 16:51:33 +01:00
committed by Marie
parent bc1cbef168
commit d2ef9e0beb
3 changed files with 77 additions and 63 deletions

View File

@@ -8,6 +8,7 @@ and this project adheres to
## [Unreleased]
- 🐛(dimail) ignore oxadmin when importing mailboxes from dimail #986
- ✨(aliases) delete all aliases in one call #1002
- ✨(aliases) fix deleting single aliases #1002
- 🔥(plugins) remove CommuneCreation plugin

View File

@@ -6,9 +6,6 @@ Unit tests for dimail client
import logging
import re
from email.errors import HeaderParseError, NonASCIILocalPartDefect
from logging import Logger
from unittest import mock
import pytest
import responses
@@ -68,20 +65,27 @@ def test_dimail_synchronization__already_sync(dimail_token_ok):
@responses.activate
@mock.patch.object(Logger, "warning")
def test_dimail_synchronization__synchronize_mailboxes(mock_warning, dimail_token_ok):
def test_dimail_synchronization__synchronize_mailboxes(caplog, dimail_token_ok): # pylint: disable=W0613, R0914
"""A mailbox existing solely on dimail should be synchronized
upon calling sync function on its domain"""
caplog.set_level(logging.INFO)
domain = factories.MailDomainEnabledFactory()
assert not models.Mailbox.objects.exists()
existing_alias = factories.AliasFactory(domain=domain)
dimail_client = DimailAPIClient()
# Ensure successful response using "responses":
# token response in fixtures
# successful token in fixtures
mailbox_valid = {
"type": "mailbox",
"status": "ok",
"email": f"validmailbox@{domain.name}",
"givenName": "Michael",
"surName": "Roch",
"displayName": "Michael Roch",
}
mailbox_oxadmin = {
"type": "mailbox",
"status": "broken",
"email": f"oxadmin@{domain.name}",
@@ -113,7 +117,7 @@ def test_dimail_synchronization__synchronize_mailboxes(mock_warning, dimail_toke
"surName": "Vang",
"displayName": "Jean Vang",
}
mailbox_existing_username = {
mailbox_existing_alias = {
"type": "mailbox",
"status": "broken",
"email": f"{existing_alias.local_part}@{domain.name}",
@@ -126,10 +130,11 @@ def test_dimail_synchronization__synchronize_mailboxes(mock_warning, dimail_toke
re.compile(rf".*/domains/{domain.name}/mailboxes/"),
json=[
mailbox_valid,
mailbox_oxadmin,
mailbox_with_wrong_domain,
mailbox_with_invalid_domain,
mailbox_with_invalid_local_part,
mailbox_existing_username,
mailbox_existing_alias,
],
status=status.HTTP_200_OK,
content_type="application/json",
@@ -137,29 +142,25 @@ def test_dimail_synchronization__synchronize_mailboxes(mock_warning, dimail_toke
imported_mailboxes = dimail_client.import_mailboxes(domain)
# 3 imports failed: wrong domain, HeaderParseError, NonASCIILocalPartDefect
assert mock_warning.call_count == 3
# 4 imports failed: oxadmin, wrong domain, HeaderParseError, NonASCIILocalPartDefect
assert len(caplog.records) == 6
log_messages = [record.message for record in caplog.records]
# first we try to import email with a wrong domain
assert mock_warning.call_args_list[0][0] == (
"Import of email %s failed because of a wrong domain",
mailbox_with_wrong_domain["email"],
)
expected_messages = [
f"Not importing OX technical address: oxadmin@{domain.name}",
f"Import of email {mailbox_with_wrong_domain['email']} failed because of a wrong domain",
f"Import of email {mailbox_with_invalid_domain['email']} failed with error Invalid Domain",
f"Import of email {mailbox_with_invalid_local_part['email']} failed with error local-part \
contains non-ASCII characters)",
f"{existing_alias.local_part} already used in an existing alias.",
]
for message in expected_messages:
assert message in log_messages
# then we try to import email with invalid domain
invalid_mailbox_log = mock_warning.call_args_list[1][0]
assert invalid_mailbox_log[1] == mailbox_with_invalid_domain["email"]
assert isinstance(invalid_mailbox_log[2], HeaderParseError)
# finally we try to import email with non ascii local part
non_ascii_mailbox_log = mock_warning.call_args_list[2][0]
assert non_ascii_mailbox_log[1] == mailbox_with_invalid_local_part["email"]
assert isinstance(non_ascii_mailbox_log[2], NonASCIILocalPartDefect)
mailbox = models.Mailbox.objects.get()
assert mailbox.local_part == "oxadmin"
assert mailbox.status == enums.MailboxStatusChoices.ENABLED
assert imported_mailboxes == [mailbox_valid["email"]]
mailbox = models.Mailbox.objects.get()
assert mailbox.local_part == "validmailbox"
assert mailbox.status == enums.MailboxStatusChoices.ENABLED
@responses.activate

View File

@@ -375,43 +375,55 @@ class DimailAPIClient:
return self._raise_exception_for_unexpected_response(response)
dimail_mailboxes = response.json()
known_mailboxes = models.Mailbox.objects.filter(domain=domain)
known_aliases = [
known_alias.local_part
for known_alias in models.Alias.objects.filter(domain=domain)
]
people_mailboxes = models.Mailbox.objects.filter(domain=domain)
imported_mailboxes = []
for dimail_mailbox in dimail_mailboxes:
if (
dimail_mailbox["email"]
not in [str(known_mailboxes) for known_mailboxes in known_mailboxes]
and dimail_mailbox["email"].split("@")[0] not in known_aliases
):
try:
# sometimes dimail api returns email from another domain,
# so we decide to exclude this kind of email
address = Address(addr_spec=dimail_mailbox["email"])
if address.domain == domain.name:
# creates a mailbox on our end
mailbox = models.Mailbox.objects.create(
first_name=dimail_mailbox["givenName"],
last_name=dimail_mailbox["surName"],
local_part=address.username,
domain=domain,
status=enums.MailboxStatusChoices.ENABLED,
password=make_password(None), # unusable password
)
imported_mailboxes.append(str(mailbox))
else:
logger.warning(
"Import of email %s failed because of a wrong domain",
dimail_mailbox["email"],
)
except (HeaderParseError, NonASCIILocalPartDefect) as err:
try:
address = Address(addr_spec=dimail_mailbox["email"])
except (HeaderParseError, NonASCIILocalPartDefect) as error:
logger.warning(
"Import of email %s failed with error %s",
dimail_mailbox["email"],
error,
)
continue
if address.username == "oxadmin":
logger.warning(
"Not importing OX technical address: %s", dimail_mailbox["email"]
)
continue
if address.username in [
alias_.local_part
for alias_ in models.Alias.objects.filter(domain=domain)
]:
logger.warning(
"%s already used in an existing alias.",
address.username,
)
continue
if str(address) not in [
str(people_mailbox) for people_mailbox in people_mailboxes
]:
# sometimes dimail api returns email from another domain,
# so we decide to exclude this kind of email
if address.domain == domain.name:
# creates a mailbox on our end
mailbox = models.Mailbox.objects.create(
first_name=dimail_mailbox["givenName"],
last_name=dimail_mailbox["surName"],
local_part=address.username,
domain=domain,
status=enums.MailboxStatusChoices.ENABLED,
password=make_password(None), # unusable password
)
imported_mailboxes.append(str(mailbox))
else:
logger.warning(
"Import of email %s failed with error %s",
"Import of email %s failed because of a wrong domain",
dimail_mailbox["email"],
err,
)
return imported_mailboxes