2024-08-05 12:20:44 +02:00
|
|
|
"""A minimalist client to synchronize with mailbox provisioning API."""
|
|
|
|
|
|
2024-09-20 18:08:02 +02:00
|
|
|
import smtplib
|
2024-08-05 12:20:44 +02:00
|
|
|
from logging import getLogger
|
|
|
|
|
|
|
|
|
|
from django.conf import settings
|
2024-09-20 18:08:02 +02:00
|
|
|
from django.contrib.sites.models import Site
|
|
|
|
|
from django.core import exceptions, mail
|
|
|
|
|
from django.template.loader import render_to_string
|
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
2024-08-05 12:20:44 +02:00
|
|
|
|
|
|
|
|
import requests
|
|
|
|
|
from rest_framework import status
|
|
|
|
|
from urllib3.util import Retry
|
|
|
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
adapter = requests.adapters.HTTPAdapter(
|
|
|
|
|
max_retries=Retry(
|
|
|
|
|
total=4,
|
|
|
|
|
backoff_factor=0.1,
|
|
|
|
|
status_forcelist=[500, 502],
|
|
|
|
|
allowed_methods=["PATCH"],
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
session = requests.Session()
|
|
|
|
|
session.mount("http://", adapter)
|
|
|
|
|
session.mount("https://", adapter)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DimailAPIClient:
|
|
|
|
|
"""A dimail-API client."""
|
|
|
|
|
|
2024-08-26 19:10:43 +02:00
|
|
|
API_URL = settings.MAIL_PROVISIONING_API_URL
|
2024-09-09 19:03:59 +02:00
|
|
|
API_CREDENTIALS = settings.MAIL_PROVISIONING_API_CREDENTIALS
|
2024-08-26 19:10:43 +02:00
|
|
|
|
2024-09-20 16:31:41 +02:00
|
|
|
def get_headers(self, user_sub=None):
|
2024-09-09 19:03:59 +02:00
|
|
|
"""
|
|
|
|
|
Build headers dictionary. Requires MAIL_PROVISIONING_API_CREDENTIALS setting,
|
|
|
|
|
to get a token from dimail /token/ endpoint.
|
2024-09-20 16:31:41 +02:00
|
|
|
If provided, request user' sub is used for la regie to log in as this user,
|
|
|
|
|
thus allowing for more precise logs.
|
2024-09-09 19:03:59 +02:00
|
|
|
"""
|
2024-08-05 12:20:44 +02:00
|
|
|
headers = {"Content-Type": "application/json"}
|
2024-09-20 16:31:41 +02:00
|
|
|
params = None
|
|
|
|
|
|
|
|
|
|
if user_sub:
|
|
|
|
|
params = {"username": str(user_sub)}
|
2024-08-05 12:20:44 +02:00
|
|
|
|
|
|
|
|
response = requests.get(
|
2024-08-26 19:10:43 +02:00
|
|
|
f"{self.API_URL}/token/",
|
2024-09-09 19:03:59 +02:00
|
|
|
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
|
2024-09-20 16:31:41 +02:00
|
|
|
params=params,
|
2024-09-03 18:29:28 +02:00
|
|
|
timeout=20,
|
2024-08-05 12:20:44 +02:00
|
|
|
)
|
|
|
|
|
|
2024-09-06 17:18:27 +02:00
|
|
|
if response.status_code == status.HTTP_200_OK:
|
|
|
|
|
headers["Authorization"] = f"Bearer {response.json()['access_token']}"
|
|
|
|
|
logger.info("Token succesfully granted by mail-provisioning API.")
|
|
|
|
|
return headers
|
|
|
|
|
|
2024-09-03 18:29:28 +02:00
|
|
|
if response.status_code == status.HTTP_403_FORBIDDEN:
|
|
|
|
|
logger.error(
|
2024-08-30 15:49:04 +02:00
|
|
|
"[DIMAIL] 403 Forbidden: Could not retrieve a token,\
|
|
|
|
|
please check 'MAIL_PROVISIONING_API_CREDENTIALS' setting.",
|
2024-08-05 12:20:44 +02:00
|
|
|
)
|
2024-09-09 19:03:59 +02:00
|
|
|
raise exceptions.PermissionDenied(
|
|
|
|
|
"Token denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
|
|
|
|
|
)
|
2024-08-05 12:20:44 +02:00
|
|
|
|
2024-09-06 17:18:27 +02:00
|
|
|
return self.pass_dimail_unexpected_response(response)
|
2024-08-05 12:20:44 +02:00
|
|
|
|
2024-09-20 16:31:41 +02:00
|
|
|
def send_mailbox_request(self, mailbox, user_sub=None):
|
2024-08-05 12:20:44 +02:00
|
|
|
"""Send a CREATE mailbox request to mail provisioning API."""
|
|
|
|
|
|
|
|
|
|
payload = {
|
2024-09-19 18:47:08 +02:00
|
|
|
"givenName": mailbox["first_name"],
|
|
|
|
|
"surName": mailbox["last_name"],
|
|
|
|
|
"displayName": f"{mailbox['first_name']} {mailbox['last_name']}",
|
2024-08-05 12:20:44 +02:00
|
|
|
}
|
2024-09-20 16:31:41 +02:00
|
|
|
headers = self.get_headers(user_sub)
|
2024-08-05 12:20:44 +02:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
response = session.post(
|
2024-09-19 18:47:08 +02:00
|
|
|
f"{self.API_URL}/domains/{mailbox['domain']}/mailboxes/{mailbox['local_part']}/",
|
2024-08-05 12:20:44 +02:00
|
|
|
json=payload,
|
2024-09-09 19:03:59 +02:00
|
|
|
headers=headers,
|
2024-08-05 12:20:44 +02:00
|
|
|
verify=True,
|
|
|
|
|
timeout=10,
|
|
|
|
|
)
|
2024-08-26 19:10:43 +02:00
|
|
|
except requests.exceptions.ConnectionError as error:
|
2024-08-05 12:20:44 +02:00
|
|
|
logger.error(
|
|
|
|
|
"Connection error while trying to reach %s.",
|
2024-08-26 19:10:43 +02:00
|
|
|
self.API_URL,
|
|
|
|
|
exc_info=error,
|
2024-08-05 12:20:44 +02:00
|
|
|
)
|
2024-08-26 19:10:43 +02:00
|
|
|
raise error
|
2024-08-05 12:20:44 +02:00
|
|
|
|
|
|
|
|
if response.status_code == status.HTTP_201_CREATED:
|
|
|
|
|
logger.info(
|
2024-09-20 16:31:41 +02:00
|
|
|
"Mailbox successfully created on domain %s by user %s",
|
2024-09-19 18:47:08 +02:00
|
|
|
str(mailbox["domain"]),
|
2024-09-20 16:31:41 +02:00
|
|
|
user_sub,
|
2024-08-05 12:20:44 +02:00
|
|
|
)
|
2024-09-03 18:29:28 +02:00
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
if response.status_code == status.HTTP_403_FORBIDDEN:
|
2024-08-08 18:28:01 +02:00
|
|
|
raise exceptions.PermissionDenied(
|
2024-08-30 15:49:04 +02:00
|
|
|
"Permission denied. Please check your MAIL_PROVISIONING_API_CREDENTIALS."
|
2024-08-26 19:10:43 +02:00
|
|
|
)
|
|
|
|
|
|
2024-09-06 17:18:27 +02:00
|
|
|
return self.pass_dimail_unexpected_response(response)
|
|
|
|
|
|
|
|
|
|
def pass_dimail_unexpected_response(self, response):
|
|
|
|
|
"""Raise error when encountering an unexpected error in dimail."""
|
|
|
|
|
error_content = response.content.decode("utf-8")
|
|
|
|
|
|
|
|
|
|
logger.error("[DIMAIL] unexpected error : %s", error_content)
|
|
|
|
|
raise SystemError(f"Unexpected response from dimail: {error_content}")
|
2024-09-20 18:08:02 +02:00
|
|
|
|
|
|
|
|
def send_new_mailbox_notification(self, recipient, mailbox_data):
|
|
|
|
|
"""
|
|
|
|
|
Send email to confirm mailbox creation
|
|
|
|
|
and send new mailbox information.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
template_vars = {
|
|
|
|
|
"title": _("Your new mailbox information"),
|
|
|
|
|
"site": Site.objects.get_current(),
|
|
|
|
|
"webmail_url": settings.WEBMAIL_URL,
|
|
|
|
|
"mailbox_data": mailbox_data,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg_html = render_to_string("mail/html/new_mailbox.html", template_vars)
|
|
|
|
|
msg_plain = render_to_string("mail/text/new_mailbox.txt", template_vars)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
mail.send_mail(
|
|
|
|
|
template_vars["title"],
|
|
|
|
|
msg_plain,
|
|
|
|
|
settings.EMAIL_FROM,
|
|
|
|
|
[recipient],
|
|
|
|
|
html_message=msg_html,
|
|
|
|
|
fail_silently=False,
|
|
|
|
|
)
|
|
|
|
|
logger.info(
|
|
|
|
|
"Information for mailbox %s sent to %s.",
|
|
|
|
|
mailbox_data["email"],
|
|
|
|
|
recipient,
|
|
|
|
|
)
|
|
|
|
|
except smtplib.SMTPException as exception:
|
|
|
|
|
logger.error(
|
|
|
|
|
"Mailbox confirmation email to %s was not sent: %s",
|
|
|
|
|
recipient,
|
|
|
|
|
exception,
|
|
|
|
|
)
|